mirror of
https://github.com/wulkanowy/wulkanowy.git
synced 2025-01-31 19:42:44 +01:00
Add log viewer (#686)
This commit is contained in:
parent
9a87df7315
commit
00f5b9431e
@ -15,7 +15,7 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "io.github.wulkanowy"
|
applicationId "io.github.wulkanowy"
|
||||||
testApplicationId "io.github.tests.wulkanowy"
|
testApplicationId "io.github.tests.wulkanowy"
|
||||||
minSdkVersion 16
|
minSdkVersion 17
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionCode 52
|
versionCode 52
|
||||||
versionName "0.15.0"
|
versionName "0.15.0"
|
||||||
@ -173,6 +173,7 @@ dependencies {
|
|||||||
implementation "com.jakewharton.threetenabp:threetenabp:1.2.2"
|
implementation "com.jakewharton.threetenabp:threetenabp:1.2.2"
|
||||||
implementation "com.jakewharton.timber:timber:4.7.1"
|
implementation "com.jakewharton.timber:timber:4.7.1"
|
||||||
implementation "at.favre.lib:slf4j-timber:1.0.1"
|
implementation "at.favre.lib:slf4j-timber:1.0.1"
|
||||||
|
implementation "fr.bipi.treessence:treessence:0.3.0"
|
||||||
implementation "com.mikepenz:aboutlibraries-core:7.1.0"
|
implementation "com.mikepenz:aboutlibraries-core:7.1.0"
|
||||||
implementation 'com.wdullaer:materialdatetimepicker:4.2.3'
|
implementation 'com.wdullaer:materialdatetimepicker:4.2.3'
|
||||||
|
|
||||||
|
@ -5,13 +5,12 @@ package io.github.wulkanowy.utils
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
fun initCrashlytics(context: Context, appInfo: AppInfo) {
|
fun initCrashlytics(context: Context, appInfo: AppInfo) {}
|
||||||
// do nothing
|
|
||||||
|
open class TimberTreeNoOp : Timber.Tree() {
|
||||||
|
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CrashlyticsTree : Timber.Tree() {
|
class CrashlyticsTree : TimberTreeNoOp()
|
||||||
|
|
||||||
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
|
class CrashlyticsExceptionTree : TimberTreeNoOp()
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -43,8 +43,8 @@
|
|||||||
android:name=".ui.modules.message.send.SendMessageActivity"
|
android:name=".ui.modules.message.send.SendMessageActivity"
|
||||||
android:configChanges="orientation|screenSize"
|
android:configChanges="orientation|screenSize"
|
||||||
android:label="@string/send_message_title"
|
android:label="@string/send_message_title"
|
||||||
android:windowSoftInputMode="adjustResize"
|
android:theme="@style/WulkanowyTheme.NoActionBar"
|
||||||
android:theme="@style/WulkanowyTheme.NoActionBar" />
|
android:windowSoftInputMode="adjustResize" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.modules.timetablewidget.TimetableWidgetConfigureActivity"
|
android:name=".ui.modules.timetablewidget.TimetableWidgetConfigureActivity"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
@ -96,6 +96,16 @@
|
|||||||
android:exported="false"
|
android:exported="false"
|
||||||
tools:node="remove" />
|
tools:node="remove" />
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:authorities="${applicationId}.fileprovider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/provider_paths" />
|
||||||
|
</provider>
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="io.fabric.ApiKey"
|
android:name="io.fabric.ApiKey"
|
||||||
android:value="${fabric_api_key}" />
|
android:value="${fabric_api_key}" />
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package io.github.wulkanowy
|
package io.github.wulkanowy
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.util.Log.DEBUG
|
||||||
import android.util.Log.INFO
|
import android.util.Log.INFO
|
||||||
import android.util.Log.VERBOSE
|
import android.util.Log.VERBOSE
|
||||||
import androidx.multidex.MultiDex
|
import androidx.multidex.MultiDex
|
||||||
@ -11,11 +12,13 @@ import dagger.android.AndroidInjector
|
|||||||
import dagger.android.support.DaggerApplication
|
import dagger.android.support.DaggerApplication
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
import eu.davidea.flexibleadapter.utils.Log
|
import eu.davidea.flexibleadapter.utils.Log
|
||||||
|
import fr.bipi.tressence.file.FileLoggerTree
|
||||||
import io.github.wulkanowy.di.DaggerAppComponent
|
import io.github.wulkanowy.di.DaggerAppComponent
|
||||||
import io.github.wulkanowy.services.sync.SyncWorkerFactory
|
import io.github.wulkanowy.services.sync.SyncWorkerFactory
|
||||||
import io.github.wulkanowy.ui.base.ThemeManager
|
import io.github.wulkanowy.ui.base.ThemeManager
|
||||||
import io.github.wulkanowy.utils.ActivityLifecycleLogger
|
import io.github.wulkanowy.utils.ActivityLifecycleLogger
|
||||||
import io.github.wulkanowy.utils.AppInfo
|
import io.github.wulkanowy.utils.AppInfo
|
||||||
|
import io.github.wulkanowy.utils.CrashlyticsExceptionTree
|
||||||
import io.github.wulkanowy.utils.CrashlyticsTree
|
import io.github.wulkanowy.utils.CrashlyticsTree
|
||||||
import io.github.wulkanowy.utils.DebugLogTree
|
import io.github.wulkanowy.utils.DebugLogTree
|
||||||
import io.github.wulkanowy.utils.initCrashlytics
|
import io.github.wulkanowy.utils.initCrashlytics
|
||||||
@ -54,9 +57,17 @@ class WulkanowyApp : DaggerApplication(), Configuration.Provider {
|
|||||||
|
|
||||||
private fun initLogging() {
|
private fun initLogging() {
|
||||||
if (appInfo.isDebug) {
|
if (appInfo.isDebug) {
|
||||||
Timber.plant(DebugLogTree())
|
|
||||||
FlexibleAdapter.enableLogs(Log.Level.DEBUG)
|
FlexibleAdapter.enableLogs(Log.Level.DEBUG)
|
||||||
|
Timber.plant(DebugLogTree())
|
||||||
|
Timber.plant(FileLoggerTree.Builder()
|
||||||
|
.withFileName("wulkanowy.%g.log")
|
||||||
|
.withDirName(applicationContext.filesDir.absolutePath)
|
||||||
|
.withFileLimit(10)
|
||||||
|
.withMinPriority(DEBUG)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
|
Timber.plant(CrashlyticsExceptionTree())
|
||||||
Timber.plant(CrashlyticsTree())
|
Timber.plant(CrashlyticsTree())
|
||||||
}
|
}
|
||||||
registerActivityLifecycleCallbacks(ActivityLifecycleLogger())
|
registerActivityLifecycleCallbacks(ActivityLifecycleLogger())
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
package io.github.wulkanowy.data.repositories.logger
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import io.reactivex.Single
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileNotFoundException
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class LoggerRepository @Inject constructor(private val context: Context) {
|
||||||
|
|
||||||
|
fun getLastLogLines(): Single<List<String>> {
|
||||||
|
return getLastModified()
|
||||||
|
.map { it.readText() }
|
||||||
|
.map { it.split("\n") }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLogFiles(): Single<List<File>> {
|
||||||
|
return Single.fromCallable {
|
||||||
|
File(context.filesDir.absolutePath).listFiles(File::isFile)?.filter {
|
||||||
|
it.name.endsWith(".log")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLastModified(): Single<File> {
|
||||||
|
return Single.fromCallable {
|
||||||
|
var lastModifiedTime = Long.MIN_VALUE
|
||||||
|
var chosenFile: File? = null
|
||||||
|
File(context.filesDir.absolutePath).listFiles(File::isFile)?.forEach { file ->
|
||||||
|
if (file.lastModified() > lastModifiedTime) {
|
||||||
|
lastModifiedTime = file.lastModified()
|
||||||
|
chosenFile = file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chosenFile == null) throw FileNotFoundException("Log file not found")
|
||||||
|
chosenFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,7 @@ import io.github.wulkanowy.R
|
|||||||
import io.github.wulkanowy.ui.base.BaseFragment
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
import io.github.wulkanowy.ui.modules.about.creator.CreatorFragment
|
import io.github.wulkanowy.ui.modules.about.creator.CreatorFragment
|
||||||
import io.github.wulkanowy.ui.modules.about.license.LicenseFragment
|
import io.github.wulkanowy.ui.modules.about.license.LicenseFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.about.logviewer.LogViewerFragment
|
||||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import io.github.wulkanowy.utils.AppInfo
|
import io.github.wulkanowy.utils.AppInfo
|
||||||
@ -110,6 +111,10 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun openLogViewer() {
|
||||||
|
if (appInfo.isDebug) (activity as? MainActivity)?.pushView(LogViewerFragment.newInstance())
|
||||||
|
}
|
||||||
|
|
||||||
override fun openDiscordInvite() {
|
override fun openDiscordInvite() {
|
||||||
context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage)
|
context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage)
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,11 @@ class AboutPresenter @Inject constructor(
|
|||||||
if (item !is AboutItem) return
|
if (item !is AboutItem) return
|
||||||
view?.run {
|
view?.run {
|
||||||
when (item.title) {
|
when (item.title) {
|
||||||
|
versionRes?.first -> {
|
||||||
|
Timber.i("Opening log viewer")
|
||||||
|
openLogViewer()
|
||||||
|
analytics.logEvent("about_open", "name" to "log_viewer")
|
||||||
|
}
|
||||||
feedbackRes?.first -> {
|
feedbackRes?.first -> {
|
||||||
Timber.i("Opening email client")
|
Timber.i("Opening email client")
|
||||||
openEmailClient()
|
openEmailClient()
|
||||||
|
@ -25,6 +25,8 @@ interface AboutView : BaseView {
|
|||||||
|
|
||||||
fun updateData(header: AboutScrollableHeader, items: List<AboutItem>)
|
fun updateData(header: AboutScrollableHeader, items: List<AboutItem>)
|
||||||
|
|
||||||
|
fun openLogViewer()
|
||||||
|
|
||||||
fun openDiscordInvite()
|
fun openDiscordInvite()
|
||||||
|
|
||||||
fun openEmailClient()
|
fun openEmailClient()
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.about.logviewer
|
||||||
|
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
|
||||||
|
class LogViewerAdapter : RecyclerView.Adapter<LogViewerAdapter.ViewHolder>() {
|
||||||
|
|
||||||
|
var lines = emptyList<String>()
|
||||||
|
|
||||||
|
class ViewHolder(val textView: TextView) : RecyclerView.ViewHolder(textView)
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
|
return ViewHolder(TextView(parent.context))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount() = lines.size
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
holder.textView.text = lines[position]
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.about.logviewer
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.Intent.EXTRA_EMAIL
|
||||||
|
import android.content.Intent.EXTRA_STREAM
|
||||||
|
import android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build.VERSION.SDK_INT
|
||||||
|
import android.os.Build.VERSION_CODES.LOLLIPOP
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuInflater
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import io.github.wulkanowy.BuildConfig.APPLICATION_ID
|
||||||
|
import io.github.wulkanowy.R
|
||||||
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
|
import kotlinx.android.synthetic.main.fragment_logviewer.*
|
||||||
|
import java.io.File
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class LogViewerFragment : BaseFragment(), LogViewerView, MainView.TitledView {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var presenter: LogViewerPresenter
|
||||||
|
|
||||||
|
private val logAdapter = LogViewerAdapter()
|
||||||
|
|
||||||
|
override val titleStringId: Int
|
||||||
|
get() = R.string.logviewer_title
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun newInstance() = LogViewerFragment()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setHasOptionsMenu(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
return inflater.inflate(R.layout.fragment_logviewer, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
|
super.onActivityCreated(savedInstanceState)
|
||||||
|
messageContainer = logViewerRecycler
|
||||||
|
presenter.onAttachView(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
inflater.inflate(R.menu.action_menu_logviewer, menu)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
return if (item.itemId == R.id.logViewerMenuShare) presenter.onShareLogsSelected()
|
||||||
|
else false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun initView() {
|
||||||
|
with(logViewerRecycler) {
|
||||||
|
layoutManager = LinearLayoutManager(context)
|
||||||
|
adapter = logAdapter
|
||||||
|
}
|
||||||
|
|
||||||
|
logViewRefreshButton.setOnClickListener { presenter.onRefreshClick() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setLines(lines: List<String>) {
|
||||||
|
logAdapter.lines = lines
|
||||||
|
logAdapter.notifyDataSetChanged()
|
||||||
|
logViewerRecycler.scrollToPosition(lines.size - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun shareLogs(files: List<File>) {
|
||||||
|
val intent = Intent(Intent.ACTION_SEND_MULTIPLE).apply {
|
||||||
|
type = "text/plain"
|
||||||
|
putExtra(EXTRA_EMAIL, arrayOf("wulkanowyinc@gmail.com"))
|
||||||
|
addFlags(FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
putParcelableArrayListExtra(EXTRA_STREAM, ArrayList(files.map {
|
||||||
|
if (SDK_INT < LOLLIPOP) Uri.fromFile(it)
|
||||||
|
else FileProvider.getUriForFile(requireContext(), "$APPLICATION_ID.fileprovider", it)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
startActivity(Intent.createChooser(intent, getString(R.string.logviewer_share)))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
presenter.onDetachView()
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.about.logviewer
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.repositories.logger.LoggerRepository
|
||||||
|
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||||
|
import io.github.wulkanowy.ui.base.BasePresenter
|
||||||
|
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||||
|
import io.github.wulkanowy.utils.SchedulersProvider
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class LogViewerPresenter @Inject constructor(
|
||||||
|
schedulers: SchedulersProvider,
|
||||||
|
errorHandler: ErrorHandler,
|
||||||
|
studentRepository: StudentRepository,
|
||||||
|
private val loggerRepository: LoggerRepository
|
||||||
|
) : BasePresenter<LogViewerView>(errorHandler, studentRepository, schedulers) {
|
||||||
|
|
||||||
|
override fun onAttachView(view: LogViewerView) {
|
||||||
|
super.onAttachView(view)
|
||||||
|
view.initView()
|
||||||
|
loadLogFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onShareLogsSelected(): Boolean {
|
||||||
|
disposable.add(loggerRepository.getLogFiles()
|
||||||
|
.subscribeOn(schedulers.backgroundThread)
|
||||||
|
.observeOn(schedulers.mainThread)
|
||||||
|
.subscribe({
|
||||||
|
Timber.i("Loading logs files result: ${it.joinToString { it.name }}")
|
||||||
|
view?.shareLogs(it)
|
||||||
|
}, {
|
||||||
|
Timber.i("Loading logs files result: An exception occurred")
|
||||||
|
errorHandler.dispatch(it)
|
||||||
|
}))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onRefreshClick() {
|
||||||
|
loadLogFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadLogFile() {
|
||||||
|
disposable.add(loggerRepository.getLastLogLines()
|
||||||
|
.subscribeOn(schedulers.backgroundThread)
|
||||||
|
.observeOn(schedulers.mainThread)
|
||||||
|
.subscribe({
|
||||||
|
Timber.i("Loading last log file result: load ${it.size} lines")
|
||||||
|
view?.setLines(it)
|
||||||
|
}, {
|
||||||
|
Timber.i("Loading last log file result: An exception occurred")
|
||||||
|
errorHandler.dispatch(it)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.about.logviewer
|
||||||
|
|
||||||
|
import io.github.wulkanowy.ui.base.BaseView
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
interface LogViewerView : BaseView {
|
||||||
|
|
||||||
|
fun initView()
|
||||||
|
|
||||||
|
fun setLines(lines: List<String>)
|
||||||
|
|
||||||
|
fun shareLogs(files: List<File>)
|
||||||
|
}
|
@ -11,6 +11,7 @@ import io.github.wulkanowy.ui.modules.about.AboutFragment
|
|||||||
import io.github.wulkanowy.ui.modules.about.creator.CreatorFragment
|
import io.github.wulkanowy.ui.modules.about.creator.CreatorFragment
|
||||||
import io.github.wulkanowy.ui.modules.about.license.LicenseFragment
|
import io.github.wulkanowy.ui.modules.about.license.LicenseFragment
|
||||||
import io.github.wulkanowy.ui.modules.about.license.LicenseModule
|
import io.github.wulkanowy.ui.modules.about.license.LicenseModule
|
||||||
|
import io.github.wulkanowy.ui.modules.about.logviewer.LogViewerFragment
|
||||||
import io.github.wulkanowy.ui.modules.account.AccountDialog
|
import io.github.wulkanowy.ui.modules.account.AccountDialog
|
||||||
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
|
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
|
||||||
import io.github.wulkanowy.ui.modules.attendance.AttendanceModule
|
import io.github.wulkanowy.ui.modules.attendance.AttendanceModule
|
||||||
@ -121,6 +122,10 @@ abstract class MainModule {
|
|||||||
@ContributesAndroidInjector(modules = [LicenseModule::class])
|
@ContributesAndroidInjector(modules = [LicenseModule::class])
|
||||||
abstract fun bindLicenseFragment(): LicenseFragment
|
abstract fun bindLicenseFragment(): LicenseFragment
|
||||||
|
|
||||||
|
@PerFragment
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract fun bindLogViewerFragment(): LogViewerFragment
|
||||||
|
|
||||||
@PerFragment
|
@PerFragment
|
||||||
@ContributesAndroidInjector()
|
@ContributesAndroidInjector()
|
||||||
abstract fun bindCreatorsFragment(): CreatorFragment
|
abstract fun bindCreatorsFragment(): CreatorFragment
|
||||||
|
@ -18,6 +18,8 @@ class DebugLogTree : Timber.DebugTree() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Bundle?.checkSavedState() = if (this == null) "(STATE IS NULL)" else ""
|
||||||
|
|
||||||
class ActivityLifecycleLogger : Application.ActivityLifecycleCallbacks {
|
class ActivityLifecycleLogger : Application.ActivityLifecycleCallbacks {
|
||||||
|
|
||||||
override fun onActivityPaused(activity: Activity?) {
|
override fun onActivityPaused(activity: Activity?) {
|
||||||
@ -45,7 +47,7 @@ class ActivityLifecycleLogger : Application.ActivityLifecycleCallbacks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {
|
override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {
|
||||||
activity?.let { Timber.d("${it::class.java.simpleName} CREATED ${checkSavedState(savedInstanceState)}") }
|
activity?.let { Timber.d("${it::class.java.simpleName} CREATED ${savedInstanceState.checkSavedState()}") }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +55,7 @@ class ActivityLifecycleLogger : Application.ActivityLifecycleCallbacks {
|
|||||||
class FragmentLifecycleLogger @Inject constructor() : FragmentManager.FragmentLifecycleCallbacks() {
|
class FragmentLifecycleLogger @Inject constructor() : FragmentManager.FragmentLifecycleCallbacks() {
|
||||||
|
|
||||||
override fun onFragmentViewCreated(fm: FragmentManager, f: Fragment, v: View, savedInstanceState: Bundle?) {
|
override fun onFragmentViewCreated(fm: FragmentManager, f: Fragment, v: View, savedInstanceState: Bundle?) {
|
||||||
Timber.d("${f::class.java.simpleName} VIEW CREATED ${checkSavedState(savedInstanceState)}")
|
Timber.d("${f::class.java.simpleName} VIEW CREATED ${savedInstanceState.checkSavedState()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFragmentStopped(fm: FragmentManager, f: Fragment) {
|
override fun onFragmentStopped(fm: FragmentManager, f: Fragment) {
|
||||||
@ -61,7 +63,7 @@ class FragmentLifecycleLogger @Inject constructor() : FragmentManager.FragmentLi
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onFragmentCreated(fm: FragmentManager, f: Fragment, savedInstanceState: Bundle?) {
|
override fun onFragmentCreated(fm: FragmentManager, f: Fragment, savedInstanceState: Bundle?) {
|
||||||
Timber.d("${f::class.java.simpleName} CREATED ${checkSavedState(savedInstanceState)}")
|
Timber.d("${f::class.java.simpleName} CREATED ${savedInstanceState.checkSavedState()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFragmentResumed(fm: FragmentManager, f: Fragment) {
|
override fun onFragmentResumed(fm: FragmentManager, f: Fragment) {
|
||||||
@ -89,7 +91,7 @@ class FragmentLifecycleLogger @Inject constructor() : FragmentManager.FragmentLi
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onFragmentActivityCreated(fm: FragmentManager, f: Fragment, savedInstanceState: Bundle?) {
|
override fun onFragmentActivityCreated(fm: FragmentManager, f: Fragment, savedInstanceState: Bundle?) {
|
||||||
Timber.d("${f::class.java.simpleName} ACTIVITY CREATED ${checkSavedState(savedInstanceState)}")
|
Timber.d("${f::class.java.simpleName} ACTIVITY CREATED ${savedInstanceState.checkSavedState()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFragmentPaused(fm: FragmentManager, f: Fragment) {
|
override fun onFragmentPaused(fm: FragmentManager, f: Fragment) {
|
||||||
@ -100,5 +102,3 @@ class FragmentLifecycleLogger @Inject constructor() : FragmentManager.FragmentLi
|
|||||||
Timber.d("${f::class.java.simpleName} DETACHED")
|
Timber.d("${f::class.java.simpleName} DETACHED")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkSavedState(savedInstanceState: Bundle?) = if (savedInstanceState == null) "(STATE IS NULL)" else ""
|
|
||||||
|
5
app/src/main/res/drawable/ic_refresh.xml
Normal file
5
app/src/main/res/drawable/ic_refresh.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
|
||||||
|
</vector>
|
34
app/src/main/res/layout/fragment_logviewer.xml
Normal file
34
app/src/main/res/layout/fragment_logviewer.xml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
tools:context=".ui.modules.about.logviewer.LogViewerFragment">
|
||||||
|
|
||||||
|
<HorizontalScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="3dp">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/logViewerRecycler"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scrollbars="vertical" />
|
||||||
|
</HorizontalScrollView>
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/logViewRefreshButton"
|
||||||
|
style="@style/Widget.MaterialComponents.FloatingActionButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|end"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:text="@string/logviewer_refresh"
|
||||||
|
android:tint="?colorOnSecondary"
|
||||||
|
app:srcCompat="@drawable/ic_refresh" />
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
11
app/src/main/res/menu/action_menu_logviewer.xml
Normal file
11
app/src/main/res/menu/action_menu_logviewer.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/logViewerMenuShare"
|
||||||
|
android:icon="@drawable/chuck_ic_share_white_24dp"
|
||||||
|
android:orderInCategory="1"
|
||||||
|
android:title="@string/logviewer_share"
|
||||||
|
app:iconTint="@color/material_on_surface_emphasis_medium"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
</menu>
|
@ -306,6 +306,11 @@
|
|||||||
<string name="creator_avatar_description">Awatar</string>
|
<string name="creator_avatar_description">Awatar</string>
|
||||||
<string name="creator_see_more">Zobacz więcej na GitHub</string>
|
<string name="creator_see_more">Zobacz więcej na GitHub</string>
|
||||||
|
|
||||||
|
<!--Log viewer-->
|
||||||
|
<string name="logviewer_title">Przeglądarka logów</string>
|
||||||
|
<string name="logviewer_share">Share logs</string>
|
||||||
|
<string name="logviewer_refresh">Odśwież</string>
|
||||||
|
|
||||||
|
|
||||||
<!--Generic-->
|
<!--Generic-->
|
||||||
<string name="all_content">Treść</string>
|
<string name="all_content">Treść</string>
|
||||||
|
@ -295,6 +295,16 @@
|
|||||||
<string name="license_dialog_title">Лицензия</string>
|
<string name="license_dialog_title">Лицензия</string>
|
||||||
|
|
||||||
|
|
||||||
|
<!--Creators-->
|
||||||
|
<string name="creator_avatar_description">аватар</string>
|
||||||
|
<string name="creator_see_more">Смотрите больше на GitHub</string>
|
||||||
|
|
||||||
|
<!--Log viewer-->
|
||||||
|
<string name="logviewer_title">Просмотр журнала</string>
|
||||||
|
<string name="logviewer_share">Share logs</string>
|
||||||
|
<string name="logviewer_refresh">Обновление</string>
|
||||||
|
|
||||||
|
|
||||||
<!--Generic-->
|
<!--Generic-->
|
||||||
<string name="all_content">Содержание</string>
|
<string name="all_content">Содержание</string>
|
||||||
<string name="all_retry">Снова</string>
|
<string name="all_retry">Снова</string>
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
<string name="settings_title">Settings</string>
|
<string name="settings_title">Settings</string>
|
||||||
<string name="more_title">More</string>
|
<string name="more_title">More</string>
|
||||||
<string name="about_title">About</string>
|
<string name="about_title">About</string>
|
||||||
|
<string name="logviewer_title">Log viewer</string>
|
||||||
<string name="creators_title">Creators</string>
|
<string name="creators_title">Creators</string>
|
||||||
<string name="license_title">Licenses</string>
|
<string name="license_title">Licenses</string>
|
||||||
<string name="message_title">Messages</string>
|
<string name="message_title">Messages</string>
|
||||||
@ -287,6 +288,10 @@
|
|||||||
<string name="creator_avatar_description">Avatar</string>
|
<string name="creator_avatar_description">Avatar</string>
|
||||||
<string name="creator_see_more">See more on GitHub</string>
|
<string name="creator_see_more">See more on GitHub</string>
|
||||||
|
|
||||||
|
<!--Log viewer-->
|
||||||
|
<string name="logviewer_share">Share logs</string>
|
||||||
|
<string name="logviewer_refresh">Refresh</string>
|
||||||
|
|
||||||
|
|
||||||
<!--Generic-->
|
<!--Generic-->
|
||||||
<string name="all_content">Content</string>
|
<string name="all_content">Content</string>
|
||||||
|
5
app/src/main/res/xml/provider_paths.xml
Normal file
5
app/src/main/res/xml/provider_paths.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<paths>
|
||||||
|
<files-path
|
||||||
|
name="files"
|
||||||
|
path="." />
|
||||||
|
</paths>
|
@ -1,10 +1,12 @@
|
|||||||
package io.github.wulkanowy.utils
|
package io.github.wulkanowy.utils
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
import com.crashlytics.android.Crashlytics
|
import com.crashlytics.android.Crashlytics
|
||||||
import com.crashlytics.android.core.CrashlyticsCore
|
import com.crashlytics.android.core.CrashlyticsCore
|
||||||
|
import fr.bipi.tressence.crash.CrashlyticsLogExceptionTree
|
||||||
|
import fr.bipi.tressence.crash.CrashlyticsLogTree
|
||||||
import io.fabric.sdk.android.Fabric
|
import io.fabric.sdk.android.Fabric
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
fun initCrashlytics(context: Context, appInfo: AppInfo) {
|
fun initCrashlytics(context: Context, appInfo: AppInfo) {
|
||||||
Fabric.with(Fabric.Builder(context)
|
Fabric.with(Fabric.Builder(context)
|
||||||
@ -19,13 +21,6 @@ fun initCrashlytics(context: Context, appInfo: AppInfo) {
|
|||||||
.build())
|
.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
class CrashlyticsTree : Timber.Tree() {
|
class CrashlyticsTree : CrashlyticsLogTree(Log.VERBOSE)
|
||||||
|
|
||||||
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
|
class CrashlyticsExceptionTree : CrashlyticsLogExceptionTree()
|
||||||
Crashlytics.setInt("priority", priority)
|
|
||||||
Crashlytics.setString("tag", tag)
|
|
||||||
|
|
||||||
if (t == null) Crashlytics.log(message)
|
|
||||||
else Crashlytics.logException(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user