mirror of
https://github.com/wulkanowy/wulkanowy.git
synced 2025-01-19 00:26:45 -06:00
Add log viewer (#686)
This commit is contained in:
parent
9a87df7315
commit
00f5b9431e
@ -15,7 +15,7 @@ android {
|
||||
defaultConfig {
|
||||
applicationId "io.github.wulkanowy"
|
||||
testApplicationId "io.github.tests.wulkanowy"
|
||||
minSdkVersion 16
|
||||
minSdkVersion 17
|
||||
targetSdkVersion 29
|
||||
versionCode 52
|
||||
versionName "0.15.0"
|
||||
@ -173,6 +173,7 @@ dependencies {
|
||||
implementation "com.jakewharton.threetenabp:threetenabp:1.2.2"
|
||||
implementation "com.jakewharton.timber:timber:4.7.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.wdullaer:materialdatetimepicker:4.2.3'
|
||||
|
||||
|
@ -5,13 +5,12 @@ package io.github.wulkanowy.utils
|
||||
import android.content.Context
|
||||
import timber.log.Timber
|
||||
|
||||
fun initCrashlytics(context: Context, appInfo: AppInfo) {
|
||||
// do nothing
|
||||
fun initCrashlytics(context: Context, appInfo: AppInfo) {}
|
||||
|
||||
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?) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
class CrashlyticsExceptionTree : TimberTreeNoOp()
|
||||
|
@ -43,8 +43,8 @@
|
||||
android:name=".ui.modules.message.send.SendMessageActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="@string/send_message_title"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:theme="@style/WulkanowyTheme.NoActionBar" />
|
||||
android:theme="@style/WulkanowyTheme.NoActionBar"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
<activity
|
||||
android:name=".ui.modules.timetablewidget.TimetableWidgetConfigureActivity"
|
||||
android:excludeFromRecents="true"
|
||||
@ -96,6 +96,16 @@
|
||||
android:exported="false"
|
||||
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
|
||||
android:name="io.fabric.ApiKey"
|
||||
android:value="${fabric_api_key}" />
|
||||
|
@ -1,6 +1,7 @@
|
||||
package io.github.wulkanowy
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log.DEBUG
|
||||
import android.util.Log.INFO
|
||||
import android.util.Log.VERBOSE
|
||||
import androidx.multidex.MultiDex
|
||||
@ -11,11 +12,13 @@ import dagger.android.AndroidInjector
|
||||
import dagger.android.support.DaggerApplication
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.utils.Log
|
||||
import fr.bipi.tressence.file.FileLoggerTree
|
||||
import io.github.wulkanowy.di.DaggerAppComponent
|
||||
import io.github.wulkanowy.services.sync.SyncWorkerFactory
|
||||
import io.github.wulkanowy.ui.base.ThemeManager
|
||||
import io.github.wulkanowy.utils.ActivityLifecycleLogger
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.CrashlyticsExceptionTree
|
||||
import io.github.wulkanowy.utils.CrashlyticsTree
|
||||
import io.github.wulkanowy.utils.DebugLogTree
|
||||
import io.github.wulkanowy.utils.initCrashlytics
|
||||
@ -54,9 +57,17 @@ class WulkanowyApp : DaggerApplication(), Configuration.Provider {
|
||||
|
||||
private fun initLogging() {
|
||||
if (appInfo.isDebug) {
|
||||
Timber.plant(DebugLogTree())
|
||||
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 {
|
||||
Timber.plant(CrashlyticsExceptionTree())
|
||||
Timber.plant(CrashlyticsTree())
|
||||
}
|
||||
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.modules.about.creator.CreatorFragment
|
||||
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.MainView
|
||||
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() {
|
||||
context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage)
|
||||
}
|
||||
|
@ -27,6 +27,11 @@ class AboutPresenter @Inject constructor(
|
||||
if (item !is AboutItem) return
|
||||
view?.run {
|
||||
when (item.title) {
|
||||
versionRes?.first -> {
|
||||
Timber.i("Opening log viewer")
|
||||
openLogViewer()
|
||||
analytics.logEvent("about_open", "name" to "log_viewer")
|
||||
}
|
||||
feedbackRes?.first -> {
|
||||
Timber.i("Opening email client")
|
||||
openEmailClient()
|
||||
|
@ -25,6 +25,8 @@ interface AboutView : BaseView {
|
||||
|
||||
fun updateData(header: AboutScrollableHeader, items: List<AboutItem>)
|
||||
|
||||
fun openLogViewer()
|
||||
|
||||
fun openDiscordInvite()
|
||||
|
||||
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.license.LicenseFragment
|
||||
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.attendance.AttendanceFragment
|
||||
import io.github.wulkanowy.ui.modules.attendance.AttendanceModule
|
||||
@ -121,6 +122,10 @@ abstract class MainModule {
|
||||
@ContributesAndroidInjector(modules = [LicenseModule::class])
|
||||
abstract fun bindLicenseFragment(): LicenseFragment
|
||||
|
||||
@PerFragment
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindLogViewerFragment(): LogViewerFragment
|
||||
|
||||
@PerFragment
|
||||
@ContributesAndroidInjector()
|
||||
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 {
|
||||
|
||||
override fun onActivityPaused(activity: Activity?) {
|
||||
@ -45,7 +47,7 @@ class ActivityLifecycleLogger : Application.ActivityLifecycleCallbacks {
|
||||
}
|
||||
|
||||
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() {
|
||||
|
||||
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) {
|
||||
@ -61,7 +63,7 @@ class FragmentLifecycleLogger @Inject constructor() : FragmentManager.FragmentLi
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -89,7 +91,7 @@ class FragmentLifecycleLogger @Inject constructor() : FragmentManager.FragmentLi
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -100,5 +102,3 @@ class FragmentLifecycleLogger @Inject constructor() : FragmentManager.FragmentLi
|
||||
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_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-->
|
||||
<string name="all_content">Treść</string>
|
||||
|
@ -295,6 +295,16 @@
|
||||
<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-->
|
||||
<string name="all_content">Содержание</string>
|
||||
<string name="all_retry">Снова</string>
|
||||
|
@ -12,6 +12,7 @@
|
||||
<string name="settings_title">Settings</string>
|
||||
<string name="more_title">More</string>
|
||||
<string name="about_title">About</string>
|
||||
<string name="logviewer_title">Log viewer</string>
|
||||
<string name="creators_title">Creators</string>
|
||||
<string name="license_title">Licenses</string>
|
||||
<string name="message_title">Messages</string>
|
||||
@ -287,6 +288,10 @@
|
||||
<string name="creator_avatar_description">Avatar</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-->
|
||||
<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
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.crashlytics.android.Crashlytics
|
||||
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 timber.log.Timber
|
||||
|
||||
fun initCrashlytics(context: Context, appInfo: AppInfo) {
|
||||
Fabric.with(Fabric.Builder(context)
|
||||
@ -19,13 +21,6 @@ fun initCrashlytics(context: Context, appInfo: AppInfo) {
|
||||
.build())
|
||||
}
|
||||
|
||||
class CrashlyticsTree : Timber.Tree() {
|
||||
class CrashlyticsTree : CrashlyticsLogTree(Log.VERBOSE)
|
||||
|
||||
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
|
||||
Crashlytics.setInt("priority", priority)
|
||||
Crashlytics.setString("tag", tag)
|
||||
|
||||
if (t == null) Crashlytics.log(message)
|
||||
else Crashlytics.logException(t)
|
||||
}
|
||||
}
|
||||
class CrashlyticsExceptionTree : CrashlyticsLogExceptionTree()
|
||||
|
Loading…
x
Reference in New Issue
Block a user