skyline/app/src/main/java/emu/skyline/LogActivity.kt

207 lines
7.4 KiB
Kotlin
Raw Normal View History

/*
* SPDX-License-Identifier: MPL-2.0
* Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
package emu.skyline
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.KeyEvent
import android.view.Menu
import android.view.MenuItem
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SearchView
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.snackbar.Snackbar
2020-10-03 11:58:34 +02:00
import emu.skyline.adapter.GenericAdapter
import emu.skyline.adapter.HeaderViewItem
import emu.skyline.adapter.LogViewItem
import emu.skyline.utils.Settings
import kotlinx.android.synthetic.main.log_activity.*
import kotlinx.android.synthetic.main.titlebar.*
import org.json.JSONObject
import java.io.File
import java.io.FileNotFoundException
import java.io.IOException
import java.net.URL
import javax.net.ssl.HttpsURLConnection
class LogActivity : AppCompatActivity() {
/**
* The log file is used to read log entries from or to clear all entries
*/
private lateinit var logFile : File
/**
* The adapter used for adding elements from the log to [log_list]
*/
2020-10-03 11:58:34 +02:00
private val adapter = GenericAdapter()
/**
* This initializes [toolbar] and fills [log_list] with data from the logs
*/
override fun onCreate(savedInstanceState : Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.log_activity)
setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
val settings = Settings(this)
val compact = settings.logCompact
val logLevel = settings.logLevel.toInt()
2020-10-03 11:58:34 +02:00
val logLevels = resources.getStringArray(R.array.log_level)
log_list.adapter = adapter
if (!compact)
log_list.addItemDecoration(DividerItemDecoration(this, RecyclerView.VERTICAL))
try {
logFile = File(applicationContext.filesDir.canonicalPath + "/skyline.log")
adapter.setItems(logFile.readLines().mapNotNull { logLine ->
2020-10-03 11:58:34 +02:00
try {
val logMeta = logLine.split("|", limit = 3)
if (logMeta[0].startsWith("1")) {
val level = logMeta[1].toInt()
if (level > logLevel) return@mapNotNull null
2020-10-03 11:58:34 +02:00
return@mapNotNull LogViewItem(compact, "(" + logMeta[2] + ") " + logMeta[3].replace('\\', '\n'), logLevels[level])
2020-10-03 11:58:34 +02:00
} else {
return@mapNotNull HeaderViewItem(logMeta[1])
2020-10-03 11:58:34 +02:00
}
} catch (ignored : IndexOutOfBoundsException) {
} catch (ignored : NumberFormatException) {
}
null
})
} catch (e : FileNotFoundException) {
Log.w("Logger", "IO Error during access of log file: " + e.message)
Toast.makeText(applicationContext, getString(R.string.file_missing), Toast.LENGTH_LONG).show()
finish()
} catch (e : IOException) {
Log.w("Logger", "IO Error during access of log file: " + e.message)
Toast.makeText(applicationContext, getString(R.string.error) + ": ${e.localizedMessage}", Toast.LENGTH_LONG).show()
}
}
/**
* This inflates the layout for the menu [R.menu.toolbar_log] and sets up searching the logs
*/
override fun onCreateOptionsMenu(menu : Menu) : Boolean {
menuInflater.inflate(R.menu.toolbar_log, menu)
val searchView = menu.findItem(R.id.action_search_log).actionView as SearchView
searchView.isSubmitButtonEnabled = false
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query : String) : Boolean {
searchView.isIconified = false
return false
}
override fun onQueryTextChange(newText : String) : Boolean {
adapter.filter.filter(newText)
return true
}
})
return super.onCreateOptionsMenu(menu)
}
/**
* This handles menu selection for [R.id.action_clear] and [R.id.action_share_log]
*/
override fun onOptionsItemSelected(item : MenuItem) : Boolean {
return when (item.itemId) {
R.id.action_clear -> {
try {
logFile.writeText("")
} catch (e : IOException) {
Log.w("Logger", "IO Error while clearing the log file: " + e.message)
Toast.makeText(applicationContext, getString(R.string.error) + ": ${e.localizedMessage}", Toast.LENGTH_LONG).show()
}
Toast.makeText(applicationContext, getString(R.string.cleared), Toast.LENGTH_LONG).show()
finish()
true
}
R.id.action_share_log -> {
uploadAndShareLog()
true
}
else -> super.onOptionsItemSelected(item)
}
}
/**
* This uploads the logs and launches the [Intent.ACTION_SEND] intent
*/
private fun uploadAndShareLog() {
Snackbar.make(findViewById(android.R.id.content), getString(R.string.upload_logs), Snackbar.LENGTH_SHORT).show()
val shareThread = Thread {
var urlConnection : HttpsURLConnection? = null
try {
val url = URL("https://hastebin.com/documents")
urlConnection = url.openConnection() as HttpsURLConnection
urlConnection.requestMethod = "POST"
urlConnection.setRequestProperty("Host", "hastebin.com")
urlConnection.setRequestProperty("Content-Type", "application/json; charset=utf-8")
urlConnection.setRequestProperty("Referer", "https://hastebin.com/")
2020-10-03 11:58:34 +02:00
urlConnection.outputStream.bufferedWriter().use {
it.write(logFile.readText())
it.flush()
}
if (urlConnection.responseCode != 200) {
Log.e("LogUpload", "HTTPS Status Code: " + urlConnection.responseCode)
throw Exception()
}
val bufferedReader = urlConnection.inputStream.bufferedReader()
val key = JSONObject(bufferedReader.readText()).getString("key")
bufferedReader.close()
val result = "https://hastebin.com/$key"
val sharingIntent = Intent(Intent.ACTION_SEND).setType("text/plain").putExtra(Intent.EXTRA_TEXT, result)
startActivity(Intent.createChooser(sharingIntent, "Share log url with:"))
} catch (e : Exception) {
runOnUiThread { Snackbar.make(findViewById(android.R.id.content), getString(R.string.error) + ": ${e.localizedMessage}", Snackbar.LENGTH_LONG).show() }
e.printStackTrace()
} finally {
urlConnection!!.disconnect()
}
}
shareThread.start()
}
/**
* This handles on calling [onBackPressed] when [KeyEvent.KEYCODE_BUTTON_B] is lifted
*/
override fun onKeyUp(keyCode : Int, event : KeyEvent?) : Boolean {
if (keyCode == KeyEvent.KEYCODE_BUTTON_B) {
onBackPressed()
return true
}
return super.onKeyUp(keyCode, event)
}
}