diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index 69fd89f9..c34cbb8c 100644
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -36,5 +36,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index f4af26bb..7689fac6 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,5 +1,14 @@
+
+
+
@@ -31,7 +40,7 @@
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4e558fa8..48396fd9 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -26,14 +26,6 @@
-
-
-
+
+
+
+
diff --git a/app/src/main/cpp/skyline/common.cpp b/app/src/main/cpp/skyline/common.cpp
index 30f96414..0d776ad3 100644
--- a/app/src/main/cpp/skyline/common.cpp
+++ b/app/src/main/cpp/skyline/common.cpp
@@ -11,14 +11,14 @@
#include "kernel/types/KThread.h"
namespace skyline {
- Logger::Logger(const std::string &path, LogLevel configLevel) : configLevel(configLevel) {
+ Logger::Logger(const std::string &path, LogLevel configLevel) : configLevel(configLevel), start(util::GetTimeNs() / constant::NsInMillisecond) {
logFile.open(path, std::ios::trunc);
UpdateTag();
- WriteHeader("Logging started");
+ Write(LogLevel::Info, "Logging started");
}
Logger::~Logger() {
- WriteHeader("Logging ended");
+ Write(LogLevel::Info, "Logging ended");
logFile.flush();
}
@@ -33,13 +33,6 @@ namespace skyline {
logTag = std::string("emu-cpp-") + threadName;
}
- void Logger::WriteHeader(const std::string &str) {
- __android_log_write(ANDROID_LOG_INFO, "emu-cpp", str.c_str());
-
- std::lock_guard guard(mutex);
- logFile << "\0360\035" << str << '\n';
- }
-
void Logger::Write(LogLevel level, const std::string &str) {
constexpr std::array levelCharacter{'E', 'W', 'I', 'D', 'V'}; // The LogLevel as written out to a file
constexpr std::array levelAlog{ANDROID_LOG_ERROR, ANDROID_LOG_WARN, ANDROID_LOG_INFO, ANDROID_LOG_DEBUG, ANDROID_LOG_VERBOSE}; // This corresponds to LogLevel and provides its equivalent for NDK Logging
@@ -50,7 +43,7 @@ namespace skyline {
__android_log_write(levelAlog[static_cast(level)], logTag.c_str(), str.c_str());
std::lock_guard guard(mutex);
- logFile << "\0361\035" << levelCharacter[static_cast(level)] << '\035' << threadName << '\035' << str << '\n'; // We use RS (\036) and GS (\035) as our delimiters
+ logFile << '\036' << levelCharacter[static_cast(level)] << '\035' << std::dec << (util::GetTimeNs() / constant::NsInMillisecond) - start << '\035' << threadName << '\035' << str << '\n'; // We use RS (\036) and GS (\035) as our delimiters
}
DeviceState::DeviceState(kernel::OS *os, std::shared_ptr jvmManager, std::shared_ptr settings, std::shared_ptr logger)
diff --git a/app/src/main/cpp/skyline/common.h b/app/src/main/cpp/skyline/common.h
index 2615ca6b..77becb73 100644
--- a/app/src/main/cpp/skyline/common.h
+++ b/app/src/main/cpp/skyline/common.h
@@ -122,6 +122,7 @@ namespace skyline {
namespace constant {
// Time
constexpr u64 NsInSecond{1000000000}; //!< The amount of nanoseconds in a second
+ constexpr u64 NsInMillisecond{1000000}; //!< The amount of nanoseconds in a millisecond
constexpr u64 NsInDay{86400000000000UL}; //!< The amount of nanoseconds in a day
}
@@ -457,8 +458,9 @@ namespace skyline {
*/
class Logger {
private:
- std::ofstream logFile; //!< An output stream to the log file
std::mutex mutex; //!< Synchronizes all output I/O to ensure there are no races
+ std::ofstream logFile; //!< An output stream to the log file
+ u64 start; //!< A timestamp in milliseconds for when the logger was started, this is used as the base for all log timestamps
public:
enum class LogLevel {
@@ -487,11 +489,6 @@ namespace skyline {
*/
static void UpdateTag();
- /**
- * @brief Writes a header, should only be used for emulation starting and ending
- */
- void WriteHeader(const std::string &str);
-
void Write(LogLevel level, const std::string &str);
/**
diff --git a/app/src/main/java/emu/skyline/EmulationActivity.kt b/app/src/main/java/emu/skyline/EmulationActivity.kt
index 7490b7a5..a53fad25 100644
--- a/app/src/main/java/emu/skyline/EmulationActivity.kt
+++ b/app/src/main/java/emu/skyline/EmulationActivity.kt
@@ -375,7 +375,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
is AxisGuestEvent -> {
value = guestEvent.value(value)
value = if (polarity) abs(value) else -abs(value)
- value = if (guestEvent.axis == AxisId.LX || guestEvent.axis == AxisId.RX) value else -value // TODO: Test this
+ value = if (guestEvent.axis == AxisId.LX || guestEvent.axis == AxisId.RX) value else -value
setAxisValue(guestEvent.id, guestEvent.axis.ordinal, (value * Short.MAX_VALUE).toInt())
}
diff --git a/app/src/main/java/emu/skyline/LogActivity.kt b/app/src/main/java/emu/skyline/LogActivity.kt
deleted file mode 100644
index ee37305f..00000000
--- a/app/src/main/java/emu/skyline/LogActivity.kt
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * 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
-import dagger.hilt.android.AndroidEntryPoint
-import emu.skyline.adapter.GenericAdapter
-import emu.skyline.adapter.HeaderViewItem
-import emu.skyline.adapter.LogViewItem
-import emu.skyline.databinding.LogActivityBinding
-import emu.skyline.utils.Settings
-import org.json.JSONObject
-import java.io.File
-import java.io.FileNotFoundException
-import java.io.IOException
-import java.net.URL
-import javax.inject.Inject
-import javax.net.ssl.HttpsURLConnection
-
-@AndroidEntryPoint
-class LogActivity : AppCompatActivity() {
- private val binding by lazy { LogActivityBinding.inflate(layoutInflater) }
-
- /**
- * The log file is used to read log entries from or to clear all entries
- */
- private lateinit var logFile : File
-
- private val adapter = GenericAdapter()
-
- @Inject
- lateinit var settings : Settings
-
- override fun onCreate(savedInstanceState : Bundle?) {
- super.onCreate(savedInstanceState)
-
- setContentView(binding.root)
-
- setSupportActionBar(binding.titlebar.toolbar)
- supportActionBar?.setDisplayHomeAsUpEnabled(true)
-
- val compact = settings.logCompact
- val logLevel = settings.logLevel.toInt()
- val logLevels = resources.getStringArray(R.array.log_level)
-
- binding.logList.adapter = adapter
-
- if (!compact) binding.logList.addItemDecoration(DividerItemDecoration(this, RecyclerView.VERTICAL))
-
- try {
- logFile = File(applicationContext.filesDir.canonicalPath + "/skyline.log")
-
- adapter.setItems(logFile.readLines().mapNotNull { logLine ->
- try {
- val logMeta = logLine.split("|", limit = 3)
-
- if (logMeta[0].startsWith("1")) {
- val level = logMeta[1].toInt()
- if (level > logLevel) return@mapNotNull null
-
- return@mapNotNull LogViewItem(compact, "(" + logMeta[2] + ") " + logMeta[3].replace('\\', '\n'), logLevels[level])
- } else {
- return@mapNotNull HeaderViewItem(logMeta[1])
- }
- } 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/")
-
- 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)
- }
-}
diff --git a/app/src/main/java/emu/skyline/MainActivity.kt b/app/src/main/java/emu/skyline/MainActivity.kt
index a13a91d9..64c8c24e 100644
--- a/app/src/main/java/emu/skyline/MainActivity.kt
+++ b/app/src/main/java/emu/skyline/MainActivity.kt
@@ -16,6 +16,7 @@ import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.content.ContextCompat
+import androidx.core.content.FileProvider
import androidx.core.content.res.use
import androidx.core.graphics.drawable.toBitmap
import androidx.core.view.isInvisible
@@ -35,6 +36,7 @@ import emu.skyline.loader.AppEntry
import emu.skyline.loader.LoaderResult
import emu.skyline.loader.RomFormat
import emu.skyline.utils.Settings
+import emu.skyline.utils.toFile
import javax.inject.Inject
import kotlin.math.ceil
import kotlin.math.roundToInt
@@ -120,7 +122,7 @@ class MainActivity : AppCompatActivity() {
binding.swipeRefreshLayout.apply {
setProgressBackgroundColorSchemeColor(obtainStyledAttributes(intArrayOf(R.attr.colorPrimary)).use { it.getColor(0, Color.BLACK) })
setColorSchemeColors(obtainStyledAttributes(intArrayOf(R.attr.colorAccent)).use { it.getColor(0, Color.BLACK) })
- post { setDistanceToTriggerSync((binding.swipeRefreshLayout.height / 2.5f).roundToInt()) }
+ post { setDistanceToTriggerSync(binding.swipeRefreshLayout.height / 3) }
setOnRefreshListener { loadRoms(false) }
}
@@ -128,7 +130,17 @@ class MainActivity : AppCompatActivity() {
loadRoms(!settings.refreshRequired)
binding.searchBar.apply {
- binding.logIcon.setOnClickListener { startActivity(Intent(context, LogActivity::class.java)) }
+ binding.logIcon.setOnClickListener {
+ val file = applicationContext.filesDir.resolve("skyline.log")
+ if (file.length() != 0L) {
+ val intent = Intent(Intent.ACTION_SEND)
+ .setType("text/plain")
+ .putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(this@MainActivity, "skyline.emu.fileprovider", file))
+ startActivity(Intent.createChooser(intent, getString(R.string.log_share_prompt)))
+ } else {
+ Snackbar.make(this@MainActivity.findViewById(android.R.id.content), getString(R.string.logs_not_found), Snackbar.LENGTH_SHORT).show()
+ }
+ }
binding.settingsIcon.setOnClickListener { settingsCallback.launch(Intent(context, SettingsActivity::class.java)) }
binding.refreshIcon.setOnClickListener { loadRoms(false) }
addTextChangedListener(afterTextChanged = { editable ->
diff --git a/app/src/main/java/emu/skyline/adapter/LogViewItem.kt b/app/src/main/java/emu/skyline/adapter/LogViewItem.kt
deleted file mode 100644
index 86217728..00000000
--- a/app/src/main/java/emu/skyline/adapter/LogViewItem.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * SPDX-License-Identifier: MPL-2.0
- * Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
- */
-
-package emu.skyline.adapter
-
-import android.content.ClipData
-import android.content.ClipboardManager
-import android.view.ViewGroup
-import android.widget.TextView
-import android.widget.Toast
-import androidx.viewbinding.ViewBinding
-import emu.skyline.databinding.LogItemBinding
-import emu.skyline.databinding.LogItemCompactBinding
-
-data class LogBindingFactory(private val compact : Boolean) : ViewBindingFactory {
- override fun createBinding(parent : ViewGroup) = if (compact) LogCompactBinding(parent) else LogBinding(parent)
-}
-
-interface ILogBinding : ViewBinding {
- val binding : ViewBinding
-
- override fun getRoot() = binding.root
-
- val textTitle : TextView
-
- val textSubTitle : TextView?
-}
-
-class LogCompactBinding(parent : ViewGroup) : ILogBinding {
- override val binding = LogItemCompactBinding.inflate(parent.inflater(), parent, false)
-
- override val textTitle = binding.textTitle
-
- override val textSubTitle : Nothing? = null
-}
-
-class LogBinding(parent : ViewGroup) : ILogBinding {
- override val binding = LogItemBinding.inflate(parent.inflater(), parent, false)
-
- override val textTitle = binding.textTitle
-
- override val textSubTitle = binding.textSubtitle
-}
-
-data class LogViewItem(private val compact : Boolean, private val message : String, private val level : String) : GenericListItem() {
- override fun getViewBindingFactory() = LogBindingFactory(compact)
-
- override fun bind(binding : ILogBinding, position : Int) {
- binding.textTitle.text = message
- binding.textSubTitle?.text = level
-
- binding.root.setOnClickListener {
- it.context.getSystemService(ClipboardManager::class.java).setPrimaryClip(ClipData.newPlainText("Log Message", "$message ($level)"))
- Toast.makeText(it.context, "Copied to clipboard", Toast.LENGTH_LONG).show()
- }
- }
-}
diff --git a/app/src/main/res/layout/log_activity.xml b/app/src/main/res/layout/log_activity.xml
deleted file mode 100644
index 8ae0cb0a..00000000
--- a/app/src/main/res/layout/log_activity.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/log_item.xml b/app/src/main/res/layout/log_item.xml
deleted file mode 100644
index ea320a37..00000000
--- a/app/src/main/res/layout/log_item.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/log_item_compact.xml b/app/src/main/res/layout/log_item_compact.xml
deleted file mode 100644
index 399100da..00000000
--- a/app/src/main/res/layout/log_item_compact.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/main_activity.xml b/app/src/main/res/layout/main_activity.xml
index 44c1cff7..a44d34b9 100644
--- a/app/src/main/res/layout/main_activity.xml
+++ b/app/src/main/res/layout/main_activity.xml
@@ -53,7 +53,7 @@
android:layout_gravity="center_vertical"
android:layout_marginEnd="4dp"
android:background="?attr/selectableItemBackgroundBorderless"
- android:contentDescription="@string/log"
+ android:contentDescription="@string/share_logs"
android:padding="5dp"
app:layout_constraintBottom_toBottomOf="@id/sub_text"
app:layout_constraintEnd_toStartOf="@id/settings_icon"
diff --git a/app/src/main/res/menu/toolbar_log.xml b/app/src/main/res/menu/toolbar_log.xml
deleted file mode 100644
index b812f0f1..00000000
--- a/app/src/main/res/menu/toolbar_log.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 24a6172c..fdd0de05 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -5,8 +5,11 @@
An error has occurred
Settings
- Logger
+ Share Logs
Refresh
+
+ Share log file
+ No logs were created during the last run
All
Metadata Missing
@@ -18,13 +21,6 @@
Invalid file
Missing title key
Incomplete production keys
-
- Clear
- Share
-
- The log file was not found
- The logs are being uploaded
- The logs have been cleared
Emulator
Search Location
@@ -56,8 +52,8 @@
Display
Force Triple Buffering
- Utilize at least 3 swapchain buffers (Higher FPS with higher input lag)
- Utilize at least 2 swapchain buffers (Lower FPS with lower input lag)
+ Utilize at least three swapchain buffers (Higher FPS but more input lag)
+ Utilize at least two swapchain buffers (Lower FPS but less input lag)
Disable Frame Throttling
Game is allowed to submit frames as fast as possible (Only for benchmarking)
Only allow the game to submit frames at the display refresh rate
diff --git a/app/src/main/res/xml/filepaths.xml b/app/src/main/res/xml/filepaths.xml
new file mode 100644
index 00000000..4faddd29
--- /dev/null
+++ b/app/src/main/res/xml/filepaths.xml
@@ -0,0 +1,4 @@
+
+
+
+