Improvements to UI/UX

This commit makes a few improvements to the UI/UX:
* Crop Game Icons to ImageView
* Controller Support for Game List
* EmulationActivity is fullscreen now
This commit is contained in:
◱ PixelyIon 2020-04-24 17:09:13 +05:30
parent f909c00e31
commit 4d787c904e
22 changed files with 232 additions and 182 deletions

View File

@ -2,7 +2,7 @@
# For more details, see # For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html # http://developer.android.com/guide/developing/tools/proguard.html
-keep class emu.skyline.loader.TitleEntry { -keep class emu.skyline.loader.AppEntry {
void writeObject(java.io.ObjectOutputStream); void writeObject(java.io.ObjectOutputStream);
void readObject(java.io.ObjectInputStream); void readObject(java.io.ObjectInputStream);
} }

View File

@ -3,7 +3,7 @@
* Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) * Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/ */
package emu.skyline.utility package emu.skyline
import android.content.ComponentName import android.content.ComponentName
import android.content.Intent import android.content.Intent
@ -11,14 +11,13 @@ import android.content.pm.ShortcutInfo
import android.content.pm.ShortcutManager import android.content.pm.ShortcutManager
import android.graphics.drawable.Icon import android.graphics.drawable.Icon
import android.os.Bundle import android.os.Bundle
import android.view.KeyEvent
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import emu.skyline.EmulationActivity
import emu.skyline.R
import emu.skyline.adapter.AppItem import emu.skyline.adapter.AppItem
import kotlinx.android.synthetic.main.app_dialog.* import kotlinx.android.synthetic.main.app_dialog.*
@ -27,29 +26,38 @@ import kotlinx.android.synthetic.main.app_dialog.*
* *
* @param item This is used to hold the [AppItem] between instances * @param item This is used to hold the [AppItem] between instances
*/ */
class AppDialog(val item: AppItem? = null) : BottomSheetDialogFragment() { class AppDialog(val item : AppItem? = null) : BottomSheetDialogFragment() {
/** /**
* This inflates the layout of the dialog after initial view creation * This inflates the layout of the dialog after initial view creation
*/ */
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater : LayoutInflater, container : ViewGroup?, savedInstanceState : Bundle?) : View? {
return requireActivity().layoutInflater.inflate(R.layout.app_dialog, container) return requireActivity().layoutInflater.inflate(R.layout.app_dialog, container)
} }
/** /**
* This expands the bottom sheet so that it's fully visible * This expands the bottom sheet so that it's fully visible and map the B button to back
*/ */
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
val behavior = BottomSheetBehavior.from(requireView().parent as View) val behavior = BottomSheetBehavior.from(requireView().parent as View)
behavior.state = BottomSheetBehavior.STATE_EXPANDED behavior.state = BottomSheetBehavior.STATE_EXPANDED
dialog?.setOnKeyListener { _, keyCode, event ->
if (keyCode == KeyEvent.KEYCODE_BUTTON_B && event.action == KeyEvent.ACTION_DOWN) {
dialog?.onBackPressed()
true
} else {
false
}
}
} }
/** /**
* This fills all the dialog with the information from [item] if it is valid and setup all user interaction * This fills all the dialog with the information from [item] if it is valid and setup all user interaction
*/ */
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState : Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
if (item is AppItem) { if (item is AppItem) {
@ -59,6 +67,13 @@ class AppDialog(val item: AppItem? = null) : BottomSheetDialogFragment() {
game_title.text = item.title game_title.text = item.title
game_subtitle.text = item.subTitle ?: getString(R.string.metadata_missing) game_subtitle.text = item.subTitle ?: getString(R.string.metadata_missing)
game_play.setOnClickListener {
val intent = Intent(activity, EmulationActivity::class.java)
intent.data = item.uri
startActivity(intent)
}
val shortcutManager = activity?.getSystemService(ShortcutManager::class.java)!! val shortcutManager = activity?.getSystemService(ShortcutManager::class.java)!!
game_pin.isEnabled = shortcutManager.isRequestPinShortcutSupported game_pin.isEnabled = shortcutManager.isRequestPinShortcutSupported
@ -76,13 +91,6 @@ class AppDialog(val item: AppItem? = null) : BottomSheetDialogFragment() {
shortcutManager.requestPinShortcut(info.build(), null) shortcutManager.requestPinShortcut(info.build(), null)
} }
game_play.setOnClickListener {
val intent = Intent(activity, EmulationActivity::class.java)
intent.data = item.uri
startActivity(intent)
}
} else } else
activity?.supportFragmentManager?.beginTransaction()?.remove(this)?.commit() activity?.supportFragmentManager?.beginTransaction()?.remove(this)?.commit()
} }

View File

@ -13,7 +13,7 @@ import android.os.ParcelFileDescriptor
import android.util.Log import android.util.Log
import android.view.Surface import android.view.Surface
import android.view.SurfaceHolder import android.view.SurfaceHolder
import android.view.WindowManager import android.view.View
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import emu.skyline.loader.getRomFormat import emu.skyline.loader.getRomFormat
@ -28,32 +28,32 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
/** /**
* The file descriptor of the ROM * The file descriptor of the ROM
*/ */
private lateinit var romFd: ParcelFileDescriptor private lateinit var romFd : ParcelFileDescriptor
/** /**
* The file descriptor of the application Preference XML * The file descriptor of the application Preference XML
*/ */
private lateinit var preferenceFd: ParcelFileDescriptor private lateinit var preferenceFd : ParcelFileDescriptor
/** /**
* The file descriptor of the Log file * The file descriptor of the Log file
*/ */
private lateinit var logFd: ParcelFileDescriptor private lateinit var logFd : ParcelFileDescriptor
/** /**
* The surface object used for displaying frames * The surface object used for displaying frames
*/ */
private var surface: Surface? = null private var surface : Surface? = null
/** /**
* A boolean flag denoting if the emulation thread should call finish() or not * A boolean flag denoting if the emulation thread should call finish() or not
*/ */
private var shouldFinish: Boolean = true private var shouldFinish : Boolean = true
/** /**
* The Kotlin thread on which emulation code executes * The Kotlin thread on which emulation code executes
*/ */
private lateinit var emulationThread: Thread private lateinit var emulationThread : Thread
/** /**
* This is the entry point into the emulation code for libskyline * This is the entry point into the emulation code for libskyline
@ -64,38 +64,38 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
* @param preferenceFd The file descriptor of the Preference XML * @param preferenceFd The file descriptor of the Preference XML
* @param logFd The file descriptor of the Log file * @param logFd The file descriptor of the Log file
*/ */
private external fun executeApplication(romUri: String, romType: Int, romFd: Int, preferenceFd: Int, logFd: Int) private external fun executeApplication(romUri : String, romType : Int, romFd : Int, preferenceFd : Int, logFd : Int)
/** /**
* This sets the halt flag in libskyline to the provided value, if set to true it causes libskyline to halt emulation * This sets the halt flag in libskyline to the provided value, if set to true it causes libskyline to halt emulation
* *
* @param halt The value to set halt to * @param halt The value to set halt to
*/ */
private external fun setHalt(halt: Boolean) private external fun setHalt(halt : Boolean)
/** /**
* This sets the surface object in libskyline to the provided value, emulation is halted if set to null * This sets the surface object in libskyline to the provided value, emulation is halted if set to null
* *
* @param surface The value to set surface to * @param surface The value to set surface to
*/ */
private external fun setSurface(surface: Surface?) private external fun setSurface(surface : Surface?)
/** /**
* This returns the current FPS of the application * This returns the current FPS of the application
*/ */
private external fun getFps(): Int private external fun getFps() : Int
/** /**
* This returns the current frame-time of the application * This returns the current frame-time of the application
*/ */
private external fun getFrametime(): Float private external fun getFrametime() : Float
/** /**
* This executes the specified ROM, [preferenceFd] and [logFd] are assumed to be valid beforehand * This executes the specified ROM, [preferenceFd] and [logFd] are assumed to be valid beforehand
* *
* @param rom The URI of the ROM to execute * @param rom The URI of the ROM to execute
*/ */
private fun executeApplication(rom: Uri) { private fun executeApplication(rom : Uri) {
val romType = getRomFormat(rom, contentResolver).ordinal val romType = getRomFormat(rom, contentResolver).ordinal
romFd = contentResolver.openFileDescriptor(rom, "r")!! romFd = contentResolver.openFileDescriptor(rom, "r")!!
@ -116,12 +116,17 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
* This makes the window fullscreen then sets up [preferenceFd] and [logFd], sets up the performance statistics and finally calls [executeApplication] for executing the application * This makes the window fullscreen then sets up [preferenceFd] and [logFd], sets up the performance statistics and finally calls [executeApplication] for executing the application
*/ */
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState : Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.emu_activity) setContentView(R.layout.emu_activity)
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN) window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_FULLSCREEN)
val preference = File("${applicationInfo.dataDir}/shared_prefs/${applicationInfo.packageName}_preferences.xml") val preference = File("${applicationInfo.dataDir}/shared_prefs/${applicationInfo.packageName}_preferences.xml")
preferenceFd = ParcelFileDescriptor.open(preference, ParcelFileDescriptor.MODE_READ_WRITE) preferenceFd = ParcelFileDescriptor.open(preference, ParcelFileDescriptor.MODE_READ_WRITE)
@ -134,7 +139,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
if (sharedPreferences.getBoolean("perf_stats", false)) { if (sharedPreferences.getBoolean("perf_stats", false)) {
lateinit var perfRunnable: Runnable lateinit var perfRunnable : Runnable
perfRunnable = Runnable { perfRunnable = Runnable {
perf_stats.text = "${getFps()} FPS\n${getFrametime()}ms" perf_stats.text = "${getFps()} FPS\n${getFrametime()}ms"
@ -150,7 +155,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
/** /**
* This is used to stop the currently executing ROM and replace it with the one specified in the new intent * This is used to stop the currently executing ROM and replace it with the one specified in the new intent
*/ */
override fun onNewIntent(intent: Intent?) { override fun onNewIntent(intent : Intent?) {
shouldFinish = false shouldFinish = false
setHalt(true) setHalt(true)
@ -184,7 +189,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
/** /**
* This sets [surface] to [holder].surface and passes it into libskyline * This sets [surface] to [holder].surface and passes it into libskyline
*/ */
override fun surfaceCreated(holder: SurfaceHolder?) { override fun surfaceCreated(holder : SurfaceHolder?) {
Log.d("surfaceCreated", "Holder: ${holder.toString()}") Log.d("surfaceCreated", "Holder: ${holder.toString()}")
surface = holder!!.surface surface = holder!!.surface
setSurface(surface) setSurface(surface)
@ -193,14 +198,14 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
/** /**
* This is purely used for debugging surface changes * This is purely used for debugging surface changes
*/ */
override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) { override fun surfaceChanged(holder : SurfaceHolder?, format : Int, width : Int, height : Int) {
Log.d("surfaceChanged", "Holder: ${holder.toString()}, Format: $format, Width: $width, Height: $height") Log.d("surfaceChanged", "Holder: ${holder.toString()}, Format: $format, Width: $width, Height: $height")
} }
/** /**
* This sets [surface] to null and passes it into libskyline * This sets [surface] to null and passes it into libskyline
*/ */
override fun surfaceDestroyed(holder: SurfaceHolder?) { override fun surfaceDestroyed(holder : SurfaceHolder?) {
Log.d("surfaceDestroyed", "Holder: ${holder.toString()}") Log.d("surfaceDestroyed", "Holder: ${holder.toString()}")
surface = null surface = null
setSurface(surface) setSurface(surface)

View File

@ -32,17 +32,17 @@ class LogActivity : AppCompatActivity() {
/** /**
* The log file is used to read log entries from or to clear all entries * The log file is used to read log entries from or to clear all entries
*/ */
private lateinit var logFile: File private lateinit var logFile : File
/** /**
* The adapter used for adding elements from the log to [log_list] * The adapter used for adding elements from the log to [log_list]
*/ */
private lateinit var adapter: LogAdapter private lateinit var adapter : LogAdapter
/** /**
* This initializes [toolbar] and fills [log_list] with data from the logs * This initializes [toolbar] and fills [log_list] with data from the logs
*/ */
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState : Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.log_activity) setContentView(R.layout.log_activity)
@ -68,12 +68,12 @@ class LogActivity : AppCompatActivity() {
logFile.forEachLine { logFile.forEachLine {
adapter.add(it) adapter.add(it)
} }
} catch (e: FileNotFoundException) { } catch (e : FileNotFoundException) {
Log.w("Logger", "IO Error during access of log file: " + e.message) Log.w("Logger", "IO Error during access of log file: " + e.message)
Toast.makeText(applicationContext, getString(R.string.file_missing), Toast.LENGTH_LONG).show() Toast.makeText(applicationContext, getString(R.string.file_missing), Toast.LENGTH_LONG).show()
finish() finish()
} catch (e: IOException) { } catch (e : IOException) {
Log.w("Logger", "IO Error during access of log file: " + e.message) 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() Toast.makeText(applicationContext, getString(R.string.error) + ": ${e.localizedMessage}", Toast.LENGTH_LONG).show()
} }
@ -82,19 +82,19 @@ class LogActivity : AppCompatActivity() {
/** /**
* This inflates the layout for the menu [R.menu.toolbar_log] and sets up searching the logs * This inflates the layout for the menu [R.menu.toolbar_log] and sets up searching the logs
*/ */
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu : Menu) : Boolean {
menuInflater.inflate(R.menu.toolbar_log, menu) menuInflater.inflate(R.menu.toolbar_log, menu)
val searchView = menu.findItem(R.id.action_search_log).actionView as SearchView val searchView = menu.findItem(R.id.action_search_log).actionView as SearchView
searchView.isSubmitButtonEnabled = false searchView.isSubmitButtonEnabled = false
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean { override fun onQueryTextSubmit(query : String) : Boolean {
searchView.isIconified = false searchView.isIconified = false
return false return false
} }
override fun onQueryTextChange(newText: String): Boolean { override fun onQueryTextChange(newText : String) : Boolean {
adapter.filter.filter(newText) adapter.filter.filter(newText)
return true return true
} }
@ -106,12 +106,12 @@ class LogActivity : AppCompatActivity() {
/** /**
* This handles menu selection for [R.id.action_clear] and [R.id.action_share_log] * This handles menu selection for [R.id.action_clear] and [R.id.action_share_log]
*/ */
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item : MenuItem) : Boolean {
return when (item.itemId) { return when (item.itemId) {
R.id.action_clear -> { R.id.action_clear -> {
try { try {
logFile.writeText("") logFile.writeText("")
} catch (e: IOException) { } catch (e : IOException) {
Log.w("Logger", "IO Error while clearing the log file: " + e.message) 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.error) + ": ${e.localizedMessage}", Toast.LENGTH_LONG).show()
} }
@ -137,7 +137,7 @@ class LogActivity : AppCompatActivity() {
Snackbar.make(findViewById(android.R.id.content), getString(R.string.upload_logs), Snackbar.LENGTH_SHORT).show() Snackbar.make(findViewById(android.R.id.content), getString(R.string.upload_logs), Snackbar.LENGTH_SHORT).show()
val shareThread = Thread(Runnable { val shareThread = Thread(Runnable {
var urlConnection: HttpsURLConnection? = null var urlConnection : HttpsURLConnection? = null
try { try {
val url = URL("https://hastebin.com/documents") val url = URL("https://hastebin.com/documents")
@ -166,7 +166,7 @@ class LogActivity : AppCompatActivity() {
val sharingIntent = Intent(Intent.ACTION_SEND).setType("text/plain").putExtra(Intent.EXTRA_TEXT, result) val sharingIntent = Intent(Intent.ACTION_SEND).setType("text/plain").putExtra(Intent.EXTRA_TEXT, result)
startActivity(Intent.createChooser(sharingIntent, "Share log url with:")) startActivity(Intent.createChooser(sharingIntent, "Share log url with:"))
} catch (e: Exception) { } catch (e : Exception) {
runOnUiThread { Snackbar.make(findViewById(android.R.id.content), getString(R.string.error) + ": ${e.localizedMessage}", Snackbar.LENGTH_LONG).show() } runOnUiThread { Snackbar.make(findViewById(android.R.id.content), getString(R.string.error) + ": ${e.localizedMessage}", Snackbar.LENGTH_LONG).show() }
e.printStackTrace() e.printStackTrace()
} finally { } finally {

View File

@ -7,6 +7,7 @@ package emu.skyline
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.pm.ActivityInfo
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
@ -29,7 +30,6 @@ import emu.skyline.adapter.GridLayoutSpan
import emu.skyline.adapter.LayoutType import emu.skyline.adapter.LayoutType
import emu.skyline.loader.BaseLoader import emu.skyline.loader.BaseLoader
import emu.skyline.loader.NroLoader import emu.skyline.loader.NroLoader
import emu.skyline.utility.AppDialog
import emu.skyline.utility.RandomAccessDocument import emu.skyline.utility.RandomAccessDocument
import kotlinx.android.synthetic.main.main_activity.* import kotlinx.android.synthetic.main.main_activity.*
import kotlinx.android.synthetic.main.titlebar.* import kotlinx.android.synthetic.main.titlebar.*
@ -42,17 +42,17 @@ class MainActivity : AppCompatActivity(), View.OnClickListener, View.OnLongClick
/** /**
* This is used to get/set shared preferences * This is used to get/set shared preferences
*/ */
private lateinit var sharedPreferences: SharedPreferences private lateinit var sharedPreferences : SharedPreferences
/** /**
* The adapter used for adding elements to [app_list] * The adapter used for adding elements to [app_list]
*/ */
private lateinit var adapter: AppAdapter private lateinit var adapter : AppAdapter
/** /**
* This adds all files in [directory] with [extension] as an entry in [adapter] using [loader] to load metadata * This adds all files in [directory] with [extension] as an entry in [adapter] using [loader] to load metadata
*/ */
private fun addEntries(extension: String, loader: BaseLoader, directory: DocumentFile, found: Boolean = false): Boolean { private fun addEntries(extension : String, loader : BaseLoader, directory : DocumentFile, found : Boolean = false) : Boolean {
var foundCurrent = found var foundCurrent = found
directory.listFiles().forEach { file -> directory.listFiles().forEach { file ->
@ -89,19 +89,22 @@ class MainActivity : AppCompatActivity(), View.OnClickListener, View.OnLongClick
* *
* @param tryLoad If this is false then trying to load cached adapter data is skipped entirely * @param tryLoad If this is false then trying to load cached adapter data is skipped entirely
*/ */
private fun refreshAdapter(tryLoad: Boolean) { private fun refreshAdapter(tryLoad : Boolean) {
if (tryLoad) { if (tryLoad) {
try { try {
adapter.load(File("${applicationInfo.dataDir}/roms.bin")) adapter.load(File("${applicationInfo.dataDir}/roms.bin"))
return return
} catch (e: Exception) { } catch (e : Exception) {
Log.w("refreshFiles", "Ran into exception while loading: ${e.message}") Log.w("refreshFiles", "Ran into exception while loading: ${e.message}")
} }
} }
thread(start = true) { thread(start = true) {
val snackbar = Snackbar.make(findViewById(android.R.id.content), getString(R.string.searching_roms), Snackbar.LENGTH_INDEFINITE) val snackbar = Snackbar.make(findViewById(android.R.id.content), getString(R.string.searching_roms), Snackbar.LENGTH_INDEFINITE)
runOnUiThread { snackbar.show() } runOnUiThread {
snackbar.show()
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED
}
try { try {
runOnUiThread { adapter.clear() } runOnUiThread { adapter.clear() }
@ -114,13 +117,13 @@ class MainActivity : AppCompatActivity(), View.OnClickListener, View.OnLongClick
try { try {
adapter.save(File("${applicationInfo.dataDir}/roms.bin")) adapter.save(File("${applicationInfo.dataDir}/roms.bin"))
} catch (e: IOException) { } catch (e : IOException) {
Log.w("refreshFiles", "Ran into exception while saving: ${e.message}") Log.w("refreshFiles", "Ran into exception while saving: ${e.message}")
} }
} }
sharedPreferences.edit().putBoolean("refresh_required", false).apply() sharedPreferences.edit().putBoolean("refresh_required", false).apply()
} catch (e: IllegalArgumentException) { } catch (e : IllegalArgumentException) {
runOnUiThread { runOnUiThread {
sharedPreferences.edit().remove("search_location").apply() sharedPreferences.edit().remove("search_location").apply()
@ -128,20 +131,23 @@ class MainActivity : AppCompatActivity(), View.OnClickListener, View.OnLongClick
finish() finish()
startActivity(intent) startActivity(intent)
} }
} catch (e: Exception) { } catch (e : Exception) {
runOnUiThread { runOnUiThread {
Snackbar.make(findViewById(android.R.id.content), getString(R.string.error) + ": ${e.localizedMessage}", Snackbar.LENGTH_SHORT).show() Snackbar.make(findViewById(android.R.id.content), getString(R.string.error) + ": ${e.localizedMessage}", Snackbar.LENGTH_SHORT).show()
} }
} }
runOnUiThread { snackbar.dismiss() } runOnUiThread {
snackbar.dismiss()
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
}
} }
} }
/** /**
* This initializes [toolbar], [open_fab], [log_fab] and [app_list] * This initializes [toolbar], [open_fab], [log_fab] and [app_list]
*/ */
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState : Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity) setContentView(R.layout.main_activity)
@ -184,6 +190,23 @@ class MainActivity : AppCompatActivity(), View.OnClickListener, View.OnLongClick
} }
} }
app_list.addOnScrollListener(object : RecyclerView.OnScrollListener() {
var y : Int = 0
override fun onScrolled(recyclerView : RecyclerView, dx : Int, dy : Int) {
y += dy
if (!app_list.isInTouchMode) {
if (y == 0)
toolbar_layout.setExpanded(true)
else
toolbar_layout.setExpanded(false)
}
super.onScrolled(recyclerView, dx, dy)
}
})
if (sharedPreferences.getString("search_location", "") == "") { if (sharedPreferences.getString("search_location", "") == "") {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.flags = Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION intent.flags = Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
@ -196,18 +219,18 @@ class MainActivity : AppCompatActivity(), View.OnClickListener, View.OnLongClick
/** /**
* This inflates the layout for the menu [R.menu.toolbar_main] and sets up searching the logs * This inflates the layout for the menu [R.menu.toolbar_main] and sets up searching the logs
*/ */
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu : Menu) : Boolean {
menuInflater.inflate(R.menu.toolbar_main, menu) menuInflater.inflate(R.menu.toolbar_main, menu)
val searchView = menu.findItem(R.id.action_search_main).actionView as SearchView val searchView = menu.findItem(R.id.action_search_main).actionView as SearchView
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean { override fun onQueryTextSubmit(query : String) : Boolean {
searchView.clearFocus() searchView.clearFocus()
return false return false
} }
override fun onQueryTextChange(newText: String): Boolean { override fun onQueryTextChange(newText : String) : Boolean {
adapter.filter.filter(newText) adapter.filter.filter(newText)
return true return true
} }
@ -219,7 +242,7 @@ class MainActivity : AppCompatActivity(), View.OnClickListener, View.OnLongClick
/** /**
* This handles on-click interaction with [R.id.log_fab], [R.id.open_fab], [R.id.app_item_linear] and [R.id.app_item_grid] * This handles on-click interaction with [R.id.log_fab], [R.id.open_fab], [R.id.app_item_linear] and [R.id.app_item_grid]
*/ */
override fun onClick(view: View) { override fun onClick(view : View) {
when (view.id) { when (view.id) {
R.id.log_fab -> startActivity(Intent(this, LogActivity::class.java)) R.id.log_fab -> startActivity(Intent(this, LogActivity::class.java))
@ -250,7 +273,7 @@ class MainActivity : AppCompatActivity(), View.OnClickListener, View.OnLongClick
/** /**
* This handles long-click interaction with [R.id.app_item_linear] and [R.id.app_item_grid] * This handles long-click interaction with [R.id.app_item_linear] and [R.id.app_item_grid]
*/ */
override fun onLongClick(view: View?): Boolean { override fun onLongClick(view : View?) : Boolean {
when (view?.id) { when (view?.id) {
R.id.app_item_linear, R.id.app_item_grid -> { R.id.app_item_linear, R.id.app_item_grid -> {
val tag = view.tag val tag = view.tag
@ -269,7 +292,7 @@ class MainActivity : AppCompatActivity(), View.OnClickListener, View.OnLongClick
/** /**
* This handles menu interaction for [R.id.action_settings] and [R.id.action_refresh] * This handles menu interaction for [R.id.action_settings] and [R.id.action_refresh]
*/ */
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item : MenuItem) : Boolean {
return when (item.itemId) { return when (item.itemId) {
R.id.action_settings -> { R.id.action_settings -> {
startActivityForResult(Intent(this, SettingsActivity::class.java), 3) startActivityForResult(Intent(this, SettingsActivity::class.java), 3)
@ -288,7 +311,7 @@ class MainActivity : AppCompatActivity(), View.OnClickListener, View.OnLongClick
/** /**
* This handles receiving activity result from [Intent.ACTION_OPEN_DOCUMENT_TREE], [Intent.ACTION_OPEN_DOCUMENT] and [SettingsActivity] * This handles receiving activity result from [Intent.ACTION_OPEN_DOCUMENT_TREE], [Intent.ACTION_OPEN_DOCUMENT] and [SettingsActivity]
*/ */
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) { override fun onActivityResult(requestCode : Int, resultCode : Int, intent : Intent?) {
super.onActivityResult(requestCode, resultCode, intent) super.onActivityResult(requestCode, resultCode, intent)
if (resultCode == RESULT_OK) { if (resultCode == RESULT_OK) {
@ -310,7 +333,7 @@ class MainActivity : AppCompatActivity(), View.OnClickListener, View.OnLongClick
startActivityForResult(intentGame, resultCode) startActivityForResult(intentGame, resultCode)
else else
startActivity(intentGame) startActivity(intentGame)
} catch (e: Exception) { } catch (e : Exception) {
Snackbar.make(findViewById(android.R.id.content), getString(R.string.error) + ": ${e.localizedMessage}", Snackbar.LENGTH_SHORT).show() Snackbar.make(findViewById(android.R.id.content), getString(R.string.error) + ": ${e.localizedMessage}", Snackbar.LENGTH_SHORT).show()
} }
} }

View File

@ -15,12 +15,12 @@ class SettingsActivity : AppCompatActivity() {
/** /**
* This is the instance of [PreferenceFragment] that is shown inside [R.id.settings] * This is the instance of [PreferenceFragment] that is shown inside [R.id.settings]
*/ */
private val preferenceFragment: PreferenceFragment = PreferenceFragment() private val preferenceFragment : PreferenceFragment = PreferenceFragment()
/** /**
* This initializes [toolbar] and [R.id.settings] * This initializes [toolbar] and [R.id.settings]
*/ */
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState : Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.settings_activity) setContentView(R.layout.settings_activity)
@ -37,7 +37,7 @@ class SettingsActivity : AppCompatActivity() {
/** /**
* This is used to refresh the preferences after [emu.skyline.preference.FolderActivity] has returned * This is used to refresh the preferences after [emu.skyline.preference.FolderActivity] has returned
*/ */
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { public override fun onActivityResult(requestCode : Int, resultCode : Int, data : Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
preferenceFragment.refreshPreferences() preferenceFragment.refreshPreferences()
} }
@ -57,7 +57,7 @@ class SettingsActivity : AppCompatActivity() {
/** /**
* This constructs the preferences from [R.xml.preferences] * This constructs the preferences from [R.xml.preferences]
*/ */
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState : Bundle?, rootKey : String?) {
setPreferencesFromResource(R.xml.preferences, rootKey) setPreferencesFromResource(R.xml.preferences, rootKey)
} }
} }

View File

@ -28,41 +28,41 @@ import emu.skyline.loader.AppEntry
/** /**
* This class is a wrapper around [AppEntry], it is used for passing around game metadata * This class is a wrapper around [AppEntry], it is used for passing around game metadata
*/ */
class AppItem(val meta: AppEntry) : BaseItem() { class AppItem(val meta : AppEntry) : BaseItem() {
/** /**
* The icon of the application * The icon of the application
*/ */
val icon: Bitmap? val icon : Bitmap?
get() = meta.icon get() = meta.icon
/** /**
* The title of the application * The title of the application
*/ */
val title: String val title : String
get() = meta.name get() = meta.name
/** /**
* The string used as the sub-title, we currently use the author * The string used as the sub-title, we currently use the author
*/ */
val subTitle: String? val subTitle : String?
get() = meta.author get() = meta.author
/** /**
* The URI of the application's image file * The URI of the application's image file
*/ */
val uri: Uri val uri : Uri
get() = meta.uri get() = meta.uri
/** /**
* The format of the application ROM as a string * The format of the application ROM as a string
*/ */
private val type: String private val type : String
get() = meta.format.name get() = meta.format.name
/** /**
* The name and author is used as the key * The name and author is used as the key
*/ */
override fun key(): String? { override fun key() : String? {
return if (meta.author != null) meta.name + " " + meta.author else meta.name return if (meta.author != null) meta.name + " " + meta.author else meta.name
} }
} }
@ -78,21 +78,21 @@ enum class LayoutType {
/** /**
* This adapter is used to display all found applications using their metadata * This adapter is used to display all found applications using their metadata
*/ */
internal class AppAdapter(val context: Context?, private val layoutType: LayoutType) : HeaderAdapter<AppItem, BaseHeader, RecyclerView.ViewHolder>(), View.OnClickListener { internal class AppAdapter(val context : Context?, private val layoutType : LayoutType) : HeaderAdapter<AppItem, BaseHeader, RecyclerView.ViewHolder>(), View.OnClickListener {
private val missingIcon = context?.resources?.getDrawable(R.drawable.default_icon, context.theme)?.toBitmap(256, 256) private val missingIcon = context?.resources?.getDrawable(R.drawable.default_icon, context.theme)?.toBitmap(256, 256)
private val missingString = context?.getString(R.string.metadata_missing) private val missingString = context?.getString(R.string.metadata_missing)
/** /**
* This adds a header to the view with the contents of [string] * This adds a header to the view with the contents of [string]
*/ */
fun addHeader(string: String) { fun addHeader(string : String) {
super.addHeader(BaseHeader(string)) super.addHeader(BaseHeader(string))
} }
/** /**
* The onClick handler for the supplied [view], used for the icon preview * The onClick handler for the supplied [view], used for the icon preview
*/ */
override fun onClick(view: View) { override fun onClick(view : View) {
val position = view.tag as Int val position = view.tag as Int
if (getItem(position) is AppItem) { if (getItem(position) is AppItem) {
@ -120,7 +120,7 @@ internal class AppAdapter(val context: Context?, private val layoutType: LayoutT
* @param title The TextView associated with the title * @param title The TextView associated with the title
* @param subtitle The TextView associated with the subtitle * @param subtitle The TextView associated with the subtitle
*/ */
private class ItemViewHolder(val parent: View, var icon: ImageView, var title: TextView, var subtitle: TextView, var card: View? = null) : RecyclerView.ViewHolder(parent) private class ItemViewHolder(val parent : View, var icon : ImageView, var title : TextView, var subtitle : TextView, var card : View? = null) : RecyclerView.ViewHolder(parent)
/** /**
* The ViewHolder used by headers is used to hold the views associated with an headers * The ViewHolder used by headers is used to hold the views associated with an headers
@ -128,14 +128,14 @@ internal class AppAdapter(val context: Context?, private val layoutType: LayoutT
* @param parent The parent view that contains all the others * @param parent The parent view that contains all the others
* @param header The TextView associated with the header * @param header The TextView associated with the header
*/ */
private class HeaderViewHolder(val parent: View, var header: TextView? = null) : RecyclerView.ViewHolder(parent) private class HeaderViewHolder(val parent : View, var header : TextView? = null) : RecyclerView.ViewHolder(parent)
/** /**
* This function creates the view-holder of type [viewType] with the layout parent as [parent] * This function creates the view-holder of type [viewType] with the layout parent as [parent]
*/ */
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent : ViewGroup, viewType : Int) : RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(context) val inflater = LayoutInflater.from(context)
var holder: RecyclerView.ViewHolder? = null var holder : RecyclerView.ViewHolder? = null
if (viewType == Item.ordinal) { if (viewType == Item.ordinal) {
val view = inflater.inflate(if (layoutType == LayoutType.List) R.layout.app_item_linear else R.layout.app_item_grid, parent, false) val view = inflater.inflate(if (layoutType == LayoutType.List) R.layout.app_item_linear else R.layout.app_item_grid, parent, false)
@ -155,6 +155,8 @@ internal class AppAdapter(val context: Context?, private val layoutType: LayoutT
if (context is View.OnLongClickListener) if (context is View.OnLongClickListener)
holder.card!!.setOnLongClickListener(context as View.OnLongClickListener) holder.card!!.setOnLongClickListener(context as View.OnLongClickListener)
holder.title.isSelected = true
} }
} else if (viewType == Header.ordinal) { } else if (viewType == Header.ordinal) {
val view = inflater.inflate(R.layout.section_item, parent, false) val view = inflater.inflate(R.layout.section_item, parent, false)
@ -169,7 +171,7 @@ internal class AppAdapter(val context: Context?, private val layoutType: LayoutT
/** /**
* This function binds the item at [position] to the supplied [viewHolder] * This function binds the item at [position] to the supplied [viewHolder]
*/ */
override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) { override fun onBindViewHolder(viewHolder : RecyclerView.ViewHolder, position : Int) {
val item = getItem(position) val item = getItem(position)
if (item is AppItem) { if (item is AppItem) {

View File

@ -20,7 +20,7 @@ import kotlin.collections.ArrayList
/** /**
* An enumeration of the type of elements in this adapter * An enumeration of the type of elements in this adapter
*/ */
enum class ElementType(val type: Int) { enum class ElementType(val type : Int) {
Header(0x0), Header(0x0),
Item(0x1) Item(0x1)
} }
@ -28,12 +28,12 @@ enum class ElementType(val type: Int) {
/** /**
* This is an abstract class that all adapter element classes inherit from * This is an abstract class that all adapter element classes inherit from
*/ */
abstract class BaseElement constructor(val elementType: ElementType) : Serializable abstract class BaseElement constructor(val elementType : ElementType) : Serializable
/** /**
* This is an abstract class that all adapter header classes inherit from * This is an abstract class that all adapter header classes inherit from
*/ */
class BaseHeader constructor(val title: String) : BaseElement(ElementType.Header) class BaseHeader constructor(val title : String) : BaseElement(ElementType.Header)
/** /**
* This is an abstract class that all adapter item classes inherit from * This is an abstract class that all adapter item classes inherit from
@ -42,7 +42,7 @@ abstract class BaseItem : BaseElement(ElementType.Item) {
/** /**
* This function returns a string used for searching * This function returns a string used for searching
*/ */
abstract fun key(): String? abstract fun key() : String?
} }
/** /**
@ -52,12 +52,12 @@ abstract class HeaderAdapter<ItemType : BaseItem?, HeaderType : BaseHeader?, Vie
/** /**
* This holds all the elements in an array even if they may not be visible * This holds all the elements in an array even if they may not be visible
*/ */
var elementArray: ArrayList<BaseElement?> = ArrayList() var elementArray : ArrayList<BaseElement?> = ArrayList()
/** /**
* This holds the indices of all the visible items in [elementArray] * This holds the indices of all the visible items in [elementArray]
*/ */
var visibleArray: ArrayList<Int> = ArrayList() var visibleArray : ArrayList<Int> = ArrayList()
/** /**
* This holds the search term if there is any, to filter any items added during a search * This holds the search term if there is any, to filter any items added during a search
@ -67,7 +67,7 @@ abstract class HeaderAdapter<ItemType : BaseItem?, HeaderType : BaseHeader?, Vie
/** /**
* This functions adds [item] to [elementArray] and [visibleArray] based on the filter * This functions adds [item] to [elementArray] and [visibleArray] based on the filter
*/ */
fun addItem(item: ItemType) { fun addItem(item : ItemType) {
elementArray.add(item) elementArray.add(item)
if (searchTerm.isNotEmpty()) if (searchTerm.isNotEmpty())
filter.filter(searchTerm) filter.filter(searchTerm)
@ -80,7 +80,7 @@ abstract class HeaderAdapter<ItemType : BaseItem?, HeaderType : BaseHeader?, Vie
/** /**
* This function adds [header] to [elementArray] and [visibleArray] based on if the filter is active * This function adds [header] to [elementArray] and [visibleArray] based on if the filter is active
*/ */
fun addHeader(header: HeaderType) { fun addHeader(header : HeaderType) {
elementArray.add(header) elementArray.add(header)
if (searchTerm.isEmpty()) if (searchTerm.isEmpty())
visibleArray.add(elementArray.size - 1) visibleArray.add(elementArray.size - 1)
@ -91,7 +91,7 @@ abstract class HeaderAdapter<ItemType : BaseItem?, HeaderType : BaseHeader?, Vie
* This serializes [elementArray] into [file] * This serializes [elementArray] into [file]
*/ */
@Throws(IOException::class) @Throws(IOException::class)
fun save(file: File) { fun save(file : File) {
val fileObj = FileOutputStream(file) val fileObj = FileOutputStream(file)
val out = ObjectOutputStream(fileObj) val out = ObjectOutputStream(fileObj)
out.writeObject(elementArray) out.writeObject(elementArray)
@ -103,7 +103,7 @@ abstract class HeaderAdapter<ItemType : BaseItem?, HeaderType : BaseHeader?, Vie
* This reads in [elementArray] from [file] * This reads in [elementArray] from [file]
*/ */
@Throws(IOException::class, ClassNotFoundException::class) @Throws(IOException::class, ClassNotFoundException::class)
open fun load(file: File) { open fun load(file : File) {
val fileObj = FileInputStream(file) val fileObj = FileInputStream(file)
val input = ObjectInputStream(fileObj) val input = ObjectInputStream(fileObj)
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
@ -125,12 +125,12 @@ abstract class HeaderAdapter<ItemType : BaseItem?, HeaderType : BaseHeader?, Vie
/** /**
* This returns the amount of elements that should be drawn to the list * This returns the amount of elements that should be drawn to the list
*/ */
override fun getItemCount(): Int = visibleArray.size override fun getItemCount() : Int = visibleArray.size
/** /**
* This returns a particular element at [position] * This returns a particular element at [position]
*/ */
fun getItem(position: Int): BaseElement? { fun getItem(position : Int) : BaseElement? {
return elementArray[visibleArray[position]] return elementArray[visibleArray[position]]
} }
@ -139,14 +139,14 @@ abstract class HeaderAdapter<ItemType : BaseItem?, HeaderType : BaseHeader?, Vie
* *
* @param position The position of the element * @param position The position of the element
*/ */
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position : Int) : Int {
return elementArray[visibleArray[position]]!!.elementType.type return elementArray[visibleArray[position]]!!.elementType.type
} }
/** /**
* This returns an instance of the filter object which is used to search for items in the view * This returns an instance of the filter object which is used to search for items in the view
*/ */
override fun getFilter(): Filter { override fun getFilter() : Filter {
return object : Filter() { return object : Filter() {
/** /**
* We use Jaro-Winkler distance for string similarity (https://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance) * We use Jaro-Winkler distance for string similarity (https://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance)
@ -164,13 +164,13 @@ abstract class HeaderAdapter<ItemType : BaseItem?, HeaderType : BaseHeader?, Vie
* @param score The score of this result * @param score The score of this result
* @param index The index of this item * @param index The index of this item
*/ */
inner class ScoredItem(val score: Double, val index: Int) {} inner class ScoredItem(val score : Double, val index : Int) {}
/** /**
* This sorts the items in [keyArray] in relation to how similar they are to [term] * This sorts the items in [keyArray] in relation to how similar they are to [term]
*/ */
fun extractSorted(term: String, keyArray: ArrayList<String>): Array<ScoredItem> { fun extractSorted(term : String, keyArray : ArrayList<String>) : Array<ScoredItem> {
val scoredItems: MutableList<ScoredItem> = ArrayList() val scoredItems : MutableList<ScoredItem> = ArrayList()
keyArray.forEachIndexed { index, item -> keyArray.forEachIndexed { index, item ->
val similarity = (jw.similarity(term, item) + cos.similarity(term, item)) / 2 val similarity = (jw.similarity(term, item) + cos.similarity(term, item)) / 2
@ -187,7 +187,7 @@ abstract class HeaderAdapter<ItemType : BaseItem?, HeaderType : BaseHeader?, Vie
/** /**
* This performs filtering on the items in [elementArray] based on similarity to [term] * This performs filtering on the items in [elementArray] based on similarity to [term]
*/ */
override fun performFiltering(term: CharSequence): FilterResults { override fun performFiltering(term : CharSequence) : FilterResults {
val results = FilterResults() val results = FilterResults()
searchTerm = (term as String).toLowerCase(Locale.getDefault()) searchTerm = (term as String).toLowerCase(Locale.getDefault())
@ -226,7 +226,7 @@ abstract class HeaderAdapter<ItemType : BaseItem?, HeaderType : BaseHeader?, Vie
/** /**
* This publishes the results that were calculated in [performFiltering] to the view * This publishes the results that were calculated in [performFiltering] to the view
*/ */
override fun publishResults(charSequence: CharSequence, results: FilterResults) { override fun publishResults(charSequence : CharSequence, results : FilterResults) {
if (results.values is ArrayList<*>) { if (results.values is ArrayList<*>) {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
visibleArray = results.values as ArrayList<Int> visibleArray = results.values as ArrayList<Int>
@ -244,11 +244,11 @@ abstract class HeaderAdapter<ItemType : BaseItem?, HeaderType : BaseHeader?, Vie
* @param adapter The adapter which is used to deduce the type of the item based on the position * @param adapter The adapter which is used to deduce the type of the item based on the position
* @param headerSpan The span size to return for headers * @param headerSpan The span size to return for headers
*/ */
class GridLayoutSpan<ItemType : BaseItem?, HeaderType : BaseHeader?, ViewHolder : RecyclerView.ViewHolder?>(val adapter: HeaderAdapter<ItemType, HeaderType, ViewHolder>, var headerSpan: Int) : GridLayoutManager.SpanSizeLookup() { class GridLayoutSpan<ItemType : BaseItem?, HeaderType : BaseHeader?, ViewHolder : RecyclerView.ViewHolder?>(val adapter : HeaderAdapter<ItemType, HeaderType, ViewHolder>, var headerSpan : Int) : GridLayoutManager.SpanSizeLookup() {
/** /**
* This returns the size of the span based on the type of the element at [position] * This returns the size of the span based on the type of the element at [position]
*/ */
override fun getSpanSize(position: Int): Int { override fun getSpanSize(position : Int) : Int {
val item = adapter.getItem(position)!! val item = adapter.getItem(position)!!
return if (item.elementType == ElementType.Item) return if (item.elementType == ElementType.Item)
1 1

View File

@ -20,11 +20,11 @@ import emu.skyline.R
/** /**
* This class is used to hold all data about a log entry * This class is used to hold all data about a log entry
*/ */
internal class LogItem(val message: String, val level: String) : BaseItem() { internal class LogItem(val message : String, val level : String) : BaseItem() {
/** /**
* The log message itself is used as the search key * The log message itself is used as the search key
*/ */
override fun key(): String? { override fun key() : String? {
return message return message
} }
} }
@ -32,13 +32,13 @@ internal class LogItem(val message: String, val level: String) : BaseItem() {
/** /**
* This adapter is used for displaying logs outputted by the application * This adapter is used for displaying logs outputted by the application
*/ */
internal class LogAdapter internal constructor(val context: Context, val compact: Boolean, private val debug_level: Int, private val level_str: Array<String>) : HeaderAdapter<LogItem, BaseHeader, RecyclerView.ViewHolder>(), OnLongClickListener { internal class LogAdapter internal constructor(val context : Context, val compact : Boolean, private val debug_level : Int, private val level_str : Array<String>) : HeaderAdapter<LogItem, BaseHeader, RecyclerView.ViewHolder>(), OnLongClickListener {
private val clipboard: ClipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager private val clipboard : ClipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
/** /**
* This function adds a line to this log adapter * This function adds a line to this log adapter
*/ */
fun add(logLine: String) { fun add(logLine : String) {
try { try {
val logMeta = logLine.split("|", limit = 3) val logMeta = logLine.split("|", limit = 3)
@ -50,8 +50,8 @@ internal class LogAdapter internal constructor(val context: Context, val compact
} else { } else {
addHeader(BaseHeader(logMeta[1])) addHeader(BaseHeader(logMeta[1]))
} }
} catch (ignored: IndexOutOfBoundsException) { } catch (ignored : IndexOutOfBoundsException) {
} catch (ignored: NumberFormatException) { } catch (ignored : NumberFormatException) {
} }
} }
@ -62,7 +62,7 @@ internal class LogAdapter internal constructor(val context: Context, val compact
* @param title The TextView associated with the title * @param title The TextView associated with the title
* @param subtitle The TextView associated with the subtitle * @param subtitle The TextView associated with the subtitle
*/ */
private class ItemViewHolder(val parent: View, var title: TextView, var subtitle: TextView? = null) : RecyclerView.ViewHolder(parent) private class ItemViewHolder(val parent : View, var title : TextView, var subtitle : TextView? = null) : RecyclerView.ViewHolder(parent)
/** /**
* The ViewHolder used by headers is used to hold the views associated with an headers * The ViewHolder used by headers is used to hold the views associated with an headers
@ -70,12 +70,12 @@ internal class LogAdapter internal constructor(val context: Context, val compact
* @param parent The parent view that contains all the others * @param parent The parent view that contains all the others
* @param header The TextView associated with the header * @param header The TextView associated with the header
*/ */
private class HeaderViewHolder(val parent: View, var header: TextView) : RecyclerView.ViewHolder(parent) private class HeaderViewHolder(val parent : View, var header : TextView) : RecyclerView.ViewHolder(parent)
/** /**
* The onLongClick handler for the supplied [view], used to copy a log into the clipboard * The onLongClick handler for the supplied [view], used to copy a log into the clipboard
*/ */
override fun onLongClick(view: View): Boolean { override fun onLongClick(view : View) : Boolean {
val item = view.tag as LogItem val item = view.tag as LogItem
clipboard.setPrimaryClip(ClipData.newPlainText("Log Message", item.message + " (" + item.level + ")")) clipboard.setPrimaryClip(ClipData.newPlainText("Log Message", item.message + " (" + item.level + ")"))
Toast.makeText(view.context, "Copied to clipboard", Toast.LENGTH_LONG).show() Toast.makeText(view.context, "Copied to clipboard", Toast.LENGTH_LONG).show()
@ -85,9 +85,9 @@ internal class LogAdapter internal constructor(val context: Context, val compact
/** /**
* This function creates the view-holder of type [viewType] with the layout parent as [parent] * This function creates the view-holder of type [viewType] with the layout parent as [parent]
*/ */
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent : ViewGroup, viewType : Int) : RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(context) val inflater = LayoutInflater.from(context)
var holder: RecyclerView.ViewHolder? = null var holder : RecyclerView.ViewHolder? = null
if (viewType == ElementType.Item.ordinal) { if (viewType == ElementType.Item.ordinal) {
if (compact) { if (compact) {
@ -112,7 +112,7 @@ internal class LogAdapter internal constructor(val context: Context, val compact
/** /**
* This function binds the item at [position] to the supplied [viewHolder] * This function binds the item at [position] to the supplied [viewHolder]
*/ */
override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) { override fun onBindViewHolder(viewHolder : RecyclerView.ViewHolder, position : Int) {
val item = getItem(position) val item = getItem(position)
if (item is LogItem) { if (item is LogItem) {

View File

@ -33,10 +33,10 @@ enum class RomFormat {
* @param uri The URL of the ROM * @param uri The URL of the ROM
* @param contentResolver The instance of ContentResolver associated with the current context * @param contentResolver The instance of ContentResolver associated with the current context
*/ */
fun getRomFormat(uri: Uri, contentResolver: ContentResolver): RomFormat { fun getRomFormat(uri : Uri, contentResolver : ContentResolver) : RomFormat {
var uriStr = "" var uriStr = ""
contentResolver.query(uri, null, null, null, null)?.use { cursor -> contentResolver.query(uri, null, null, null, null)?.use { cursor ->
val nameIndex: Int = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) val nameIndex : Int = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
cursor.moveToFirst() cursor.moveToFirst()
uriStr = cursor.getString(nameIndex) uriStr = cursor.getString(nameIndex)
} }
@ -50,26 +50,26 @@ class AppEntry : Serializable {
/** /**
* The name of the application * The name of the application
*/ */
var name: String var name : String
/** /**
* The author of the application, if it can be extracted from the metadata * The author of the application, if it can be extracted from the metadata
*/ */
var author: String? = null var author : String? = null
var icon: Bitmap? = null var icon : Bitmap? = null
/** /**
* The format of the application ROM * The format of the application ROM
*/ */
var format: RomFormat var format : RomFormat
/** /**
* The URI of the application ROM * The URI of the application ROM
*/ */
var uri: Uri var uri : Uri
constructor(name: String, author: String, format: RomFormat, uri: Uri, icon: Bitmap) { constructor(name : String, author : String, format : RomFormat, uri : Uri, icon : Bitmap) {
this.name = name this.name = name
this.author = author this.author = author
this.icon = icon this.icon = icon
@ -77,9 +77,9 @@ class AppEntry : Serializable {
this.uri = uri this.uri = uri
} }
constructor(context: Context, format: RomFormat, uri: Uri) { constructor(context : Context, format : RomFormat, uri : Uri) {
this.name = context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> this.name = context.contentResolver.query(uri, null, null, null, null)?.use { cursor ->
val nameIndex: Int = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) val nameIndex : Int = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
cursor.moveToFirst() cursor.moveToFirst()
cursor.getString(nameIndex) cursor.getString(nameIndex)
}!!.dropLast(format.name.length + 1) }!!.dropLast(format.name.length + 1)
@ -93,7 +93,7 @@ class AppEntry : Serializable {
* @param output The stream to which the object is written into * @param output The stream to which the object is written into
*/ */
@Throws(IOException::class) @Throws(IOException::class)
private fun writeObject(output: ObjectOutputStream) { private fun writeObject(output : ObjectOutputStream) {
output.writeUTF(name) output.writeUTF(name)
output.writeObject(format) output.writeObject(format)
output.writeUTF(uri.toString()) output.writeUTF(uri.toString())
@ -111,7 +111,7 @@ class AppEntry : Serializable {
* @param input The stream from which the object data is retrieved from * @param input The stream from which the object data is retrieved from
*/ */
@Throws(IOException::class, ClassNotFoundException::class) @Throws(IOException::class, ClassNotFoundException::class)
private fun readObject(input: ObjectInputStream) { private fun readObject(input : ObjectInputStream) {
name = input.readUTF() name = input.readUTF()
format = input.readObject() as RomFormat format = input.readObject() as RomFormat
uri = Uri.parse(input.readUTF()) uri = Uri.parse(input.readUTF())
@ -125,14 +125,14 @@ class AppEntry : Serializable {
/** /**
* This class is used as the base class for all loaders * This class is used as the base class for all loaders
*/ */
internal abstract class BaseLoader(val context: Context, val format: RomFormat) { internal abstract class BaseLoader(val context : Context, val format : RomFormat) {
/** /**
* This is used to get the [AppEntry] for the specified [file] at the supplied [uri] * This is used to get the [AppEntry] for the specified [file] at the supplied [uri]
*/ */
abstract fun getAppEntry(file: RandomAccessDocument, uri: Uri): AppEntry abstract fun getAppEntry(file : RandomAccessDocument, uri : Uri) : AppEntry
/** /**
* This returns if the supplied [file] is a valid ROM or not * This returns if the supplied [file] is a valid ROM or not
*/ */
abstract fun verifyFile(file: RandomAccessDocument): Boolean abstract fun verifyFile(file : RandomAccessDocument) : Boolean
} }

View File

@ -14,11 +14,11 @@ import java.io.IOException
/** /**
* This loader is used to load in NRO (Nintendo Relocatable Object) files (https://switchbrew.org/wiki/NRO) * This loader is used to load in NRO (Nintendo Relocatable Object) files (https://switchbrew.org/wiki/NRO)
*/ */
internal class NroLoader(context: Context) : BaseLoader(context, RomFormat.NRO) { internal class NroLoader(context : Context) : BaseLoader(context, RomFormat.NRO) {
/** /**
* This is used to get the [AppEntry] for the specified NRO * This is used to get the [AppEntry] for the specified NRO
*/ */
override fun getAppEntry(file: RandomAccessDocument, uri: Uri): AppEntry { override fun getAppEntry(file : RandomAccessDocument, uri : Uri) : AppEntry {
return try { return try {
file.seek(0x18) // Skip to NroHeader.size file.seek(0x18) // Skip to NroHeader.size
@ -52,7 +52,7 @@ internal class NroLoader(context: Context) : BaseLoader(context, RomFormat.NRO)
file.read(author) file.read(author)
AppEntry(String(name).substringBefore((0.toChar())), String(author).substringBefore((0.toChar())), format, uri, icon) AppEntry(String(name).substringBefore((0.toChar())), String(author).substringBefore((0.toChar())), format, uri, icon)
} catch (e: IOException) { } catch (e : IOException) {
AppEntry(context, format, uri) AppEntry(context, format, uri)
} }
} }
@ -60,14 +60,14 @@ internal class NroLoader(context: Context) : BaseLoader(context, RomFormat.NRO)
/** /**
* This verifies if [file] is a valid NRO file * This verifies if [file] is a valid NRO file
*/ */
override fun verifyFile(file: RandomAccessDocument): Boolean { override fun verifyFile(file : RandomAccessDocument) : Boolean {
try { try {
file.seek(0x10) // Skip to NroHeader.magic file.seek(0x10) // Skip to NroHeader.magic
val buffer = ByteArray(4) val buffer = ByteArray(4)
file.read(buffer) file.read(buffer)
if (String(buffer) != "NRO0") return false if (String(buffer) != "NRO0") return false
} catch (e: IOException) { } catch (e : IOException) {
return false return false
} }
return true return true

View File

@ -18,7 +18,7 @@ class FolderActivity : AppCompatActivity() {
/** /**
* This launches the [Intent.ACTION_OPEN_DOCUMENT_TREE] intent on creation * This launches the [Intent.ACTION_OPEN_DOCUMENT_TREE] intent on creation
*/ */
override fun onCreate(state: Bundle?) { override fun onCreate(state : Bundle?) {
super.onCreate(state) super.onCreate(state)
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
@ -28,7 +28,7 @@ class FolderActivity : AppCompatActivity() {
/** /**
* This changes the search location preference if the [Intent.ACTION_OPEN_DOCUMENT_TREE] has returned and [finish]es the activity * This changes the search location preference if the [Intent.ACTION_OPEN_DOCUMENT_TREE] has returned and [finish]es the activity
*/ */
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { public override fun onActivityResult(requestCode : Int, resultCode : Int, data : Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) { if (resultCode == Activity.RESULT_OK) {

View File

@ -20,15 +20,15 @@ class FolderPreference : Preference {
/** /**
* The directory the preference is currently set to * The directory the preference is currently set to
*/ */
private var mDirectory: String? = null private var mDirectory : String? = null
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { constructor(context : Context?, attrs : AttributeSet?, defStyleAttr : Int) : super(context, attrs, defStyleAttr) {
summaryProvider = SimpleSummaryProvider() summaryProvider = SimpleSummaryProvider()
} }
constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, R.attr.preferenceStyle) constructor(context : Context?, attrs : AttributeSet?) : this(context, attrs, R.attr.preferenceStyle)
constructor(context: Context?) : this(context, null) constructor(context : Context?) : this(context, null)
/** /**
* This launches [FolderActivity] on click to change the directory * This launches [FolderActivity] on click to change the directory
@ -41,7 +41,7 @@ class FolderPreference : Preference {
/** /**
* This sets the initial value of [mDirectory] * This sets the initial value of [mDirectory]
*/ */
override fun onSetInitialValue(defaultValue: Any?) { override fun onSetInitialValue(defaultValue : Any?) {
mDirectory = getPersistedString(defaultValue as String?) mDirectory = getPersistedString(defaultValue as String?)
} }
@ -52,7 +52,7 @@ class FolderPreference : Preference {
/** /**
* This returns the decoded URI of the directory as the summary * This returns the decoded URI of the directory as the summary
*/ */
override fun provideSummary(preference: FolderPreference): CharSequence { override fun provideSummary(preference : FolderPreference) : CharSequence {
return Uri.decode(preference.mDirectory) ?: "" return Uri.decode(preference.mDirectory) ?: ""
} }
} }

View File

@ -22,11 +22,11 @@ class LicenseDialog : DialogFragment() {
/** /**
* This inflates the layout of the dialog and sets the minimum width/height to 90% of the screen size * This inflates the layout of the dialog and sets the minimum width/height to 90% of the screen size
*/ */
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater : LayoutInflater, container : ViewGroup?, savedInstanceState : Bundle?) : View? {
val layout = layoutInflater.inflate(R.layout.license_dialog, container) val layout = layoutInflater.inflate(R.layout.license_dialog, container)
val displayRectangle = Rect() val displayRectangle = Rect()
val window: Window = activity!!.window val window : Window = activity!!.window
window.decorView.getWindowVisibleDisplayFrame(displayRectangle) window.decorView.getWindowVisibleDisplayFrame(displayRectangle)
layout.minimumWidth = ((displayRectangle.width() * 0.9f).toInt()) layout.minimumWidth = ((displayRectangle.width() * 0.9f).toInt())
@ -38,7 +38,7 @@ class LicenseDialog : DialogFragment() {
/** /**
* This sets the [license_url] and [license_content] based on arguments passed * This sets the [license_url] and [license_content] based on arguments passed
*/ */
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState : Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
license_url.text = arguments?.getString("libraryUrl")!! license_url.text = arguments?.getString("libraryUrl")!!

View File

@ -20,7 +20,7 @@ class LicensePreference : Preference {
/** /**
* The [FragmentManager] is used to show the [LicenseDialog] fragment * The [FragmentManager] is used to show the [LicenseDialog] fragment
*/ */
private val fragmentManager: FragmentManager private val fragmentManager : FragmentManager
/** /**
* The tag used by this preference when launching a corresponding fragment * The tag used by this preference when launching a corresponding fragment
@ -30,17 +30,17 @@ class LicensePreference : Preference {
/** /**
* The URL of the library * The URL of the library
*/ */
private var libraryUrl: String? = null private var libraryUrl : String? = null
/** /**
* The contents of the license of this library * The contents of the license of this library
*/ */
private var libraryLicense: Int? = null private var libraryLicense : Int? = null
/** /**
* The constructor assigns the [fragmentManager] from the activity and finds [libraryUrl] and [libraryLicense] in the attributes * The constructor assigns the [fragmentManager] from the activity and finds [libraryUrl] and [libraryLicense] in the attributes
*/ */
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) { constructor(context : Context?, attrs : AttributeSet?, defStyleAttr : Int, defStyleRes : Int) : super(context, attrs, defStyleAttr, defStyleRes) {
fragmentManager = (context as AppCompatActivity).supportFragmentManager fragmentManager = (context as AppCompatActivity).supportFragmentManager
for (i in 0 until attrs!!.attributeCount) { for (i in 0 until attrs!!.attributeCount) {
@ -53,11 +53,11 @@ class LicensePreference : Preference {
} }
} }
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : this(context, attrs, defStyleAttr, 0) constructor(context : Context?, attrs : AttributeSet?, defStyleAttr : Int) : this(context, attrs, defStyleAttr, 0)
constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, R.attr.dialogPreferenceStyle) constructor(context : Context?, attrs : AttributeSet?) : this(context, attrs, R.attr.dialogPreferenceStyle)
constructor(context: Context?) : this(context, null) constructor(context : Context?) : this(context, null)
/** /**
* The [LicenseDialog] fragment is shown using [fragmentManager] on click with [libraryUrl] and [libraryLicense] passed as arguments * The [LicenseDialog] fragment is shown using [fragmentManager] on click with [libraryUrl] and [libraryLicense] passed as arguments

View File

@ -14,16 +14,16 @@ import androidx.preference.ListPreference
* This preference is used to set the theme to Light/Dark mode * This preference is used to set the theme to Light/Dark mode
*/ */
class ThemePreference : ListPreference { class ThemePreference : ListPreference {
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) constructor(context : Context?, attrs : AttributeSet?, defStyleAttr : Int) : super(context, attrs, defStyleAttr)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) constructor(context : Context?, attrs : AttributeSet?) : super(context, attrs)
constructor(context: Context?) : super(context) constructor(context : Context?) : super(context)
/** /**
* This changes [AppCompatDelegate.sDefaultNightMode] based on what the user's selection is * This changes [AppCompatDelegate.sDefaultNightMode] based on what the user's selection is
*/ */
override fun callChangeListener(newValue: Any?): Boolean { override fun callChangeListener(newValue : Any?) : Boolean {
AppCompatDelegate.setDefaultNightMode(when ((newValue as String).toInt()) { AppCompatDelegate.setDefaultNightMode(when ((newValue as String).toInt()) {
0 -> AppCompatDelegate.MODE_NIGHT_NO 0 -> AppCompatDelegate.MODE_NIGHT_NO
1 -> AppCompatDelegate.MODE_NIGHT_YES 1 -> AppCompatDelegate.MODE_NIGHT_YES

View File

@ -15,7 +15,7 @@ import java.nio.ByteBuffer
* *
* @param parcelFileDescriptor The file descriptor for the [DocumentFile] * @param parcelFileDescriptor The file descriptor for the [DocumentFile]
*/ */
class RandomAccessDocument(private var parcelFileDescriptor: ParcelFileDescriptor) { class RandomAccessDocument(private var parcelFileDescriptor : ParcelFileDescriptor) {
/** /**
* The actual file descriptor for the [DocumentFile] as an [FileDescriptor] object * The actual file descriptor for the [DocumentFile] as an [FileDescriptor] object
*/ */
@ -24,19 +24,19 @@ class RandomAccessDocument(private var parcelFileDescriptor: ParcelFileDescripto
/** /**
* The current position of where the file is being read * The current position of where the file is being read
*/ */
private var position: Long = 0 private var position : Long = 0
/** /**
* The constructor sets [parcelFileDescriptor] by opening a read-only FD to [file] * The constructor sets [parcelFileDescriptor] by opening a read-only FD to [file]
*/ */
constructor(context: Context, file: DocumentFile) : this(context.contentResolver.openFileDescriptor(file.uri, "r")!!) constructor(context : Context, file : DocumentFile) : this(context.contentResolver.openFileDescriptor(file.uri, "r")!!)
/** /**
* This reads in as many as possible bytes into [array] (Generally [array].size) * This reads in as many as possible bytes into [array] (Generally [array].size)
* *
* @return The amount of bytes read from the file * @return The amount of bytes read from the file
*/ */
fun read(array: ByteArray): Int { fun read(array : ByteArray) : Int {
val bytesRead = android.system.Os.pread(fileDescriptor, array, 0, array.size, position) val bytesRead = android.system.Os.pread(fileDescriptor, array, 0, array.size, position)
position += bytesRead position += bytesRead
return bytesRead return bytesRead
@ -47,7 +47,7 @@ class RandomAccessDocument(private var parcelFileDescriptor: ParcelFileDescripto
* *
* @return The amount of bytes read from the file * @return The amount of bytes read from the file
*/ */
fun read(buffer: ByteBuffer): Int { fun read(buffer : ByteBuffer) : Int {
val bytesRead = android.system.Os.pread(fileDescriptor, buffer.array(), 0, buffer.array().size, position) val bytesRead = android.system.Os.pread(fileDescriptor, buffer.array(), 0, buffer.array().size, position)
position += bytesRead position += bytesRead
return bytesRead return bytesRead
@ -56,8 +56,8 @@ class RandomAccessDocument(private var parcelFileDescriptor: ParcelFileDescripto
/** /**
* This returns a single [Long] from the file at the current [position] * This returns a single [Long] from the file at the current [position]
*/ */
fun readLong(): Long { fun readLong() : Long {
val buffer: ByteBuffer = ByteBuffer.allocate(Long.SIZE_BYTES) val buffer : ByteBuffer = ByteBuffer.allocate(Long.SIZE_BYTES)
read(buffer) read(buffer)
return buffer.long return buffer.long
} }
@ -65,8 +65,8 @@ class RandomAccessDocument(private var parcelFileDescriptor: ParcelFileDescripto
/** /**
* This returns a single [Int] from the file at the current [position] * This returns a single [Int] from the file at the current [position]
*/ */
fun readInt(): Int { fun readInt() : Int {
val buffer: ByteBuffer = ByteBuffer.allocate(Int.SIZE_BYTES) val buffer : ByteBuffer = ByteBuffer.allocate(Int.SIZE_BYTES)
read(buffer) read(buffer)
return buffer.int return buffer.int
} }
@ -74,14 +74,14 @@ class RandomAccessDocument(private var parcelFileDescriptor: ParcelFileDescripto
/** /**
* This sets [RandomAccessDocument.position] to the supplied [position] * This sets [RandomAccessDocument.position] to the supplied [position]
*/ */
fun seek(position: Long) { fun seek(position : Long) {
this.position = position this.position = position
} }
/** /**
* This increments [position] by [amount] * This increments [position] by [amount]
*/ */
fun skipBytes(amount: Long) { fun skipBytes(amount : Long) {
this.position += amount this.position += amount
} }

View File

@ -4,6 +4,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:nextFocusRight="@id/game_play"
android:padding="16dp"> android:padding="16dp">
<com.google.android.material.imageview.ShapeableImageView <com.google.android.material.imageview.ShapeableImageView
@ -11,6 +12,7 @@
android:layout_width="150dp" android:layout_width="150dp"
android:layout_height="150dp" android:layout_height="150dp"
android:contentDescription="@string/icon" android:contentDescription="@string/icon"
android:focusable="false"
app:shapeAppearanceOverlay="@style/roundedAppImage" /> app:shapeAppearanceOverlay="@style/roundedAppImage" />
<LinearLayout <LinearLayout
@ -47,6 +49,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="6dp" android:layout_marginEnd="6dp"
android:focusedByDefault="true"
android:text="@string/play" android:text="@string/play"
app:icon="@drawable/ic_play" /> app:icon="@drawable/ic_play" />

View File

@ -16,38 +16,46 @@
card_view:cardCornerRadius="4dp"> card_view:cardCornerRadius="4dp">
<RelativeLayout <RelativeLayout
android:layout_width="wrap_content" android:layout_width="155dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:orientation="vertical"> android:orientation="vertical">
<ImageView <ImageView
android:id="@+id/icon" android:id="@+id/icon"
android:layout_width="150dp" android:layout_width="match_parent"
android:layout_height="150dp" android:layout_height="155dp"
android:layout_alignParentTop="false" android:layout_alignParentTop="false"
android:layout_centerHorizontal="true" android:layout_centerHorizontal="true"
android:contentDescription="@string/icon" /> android:contentDescription="@string/icon"
android:scaleType="centerCrop" />
<TextView <TextView
android:id="@+id/text_title" android:id="@+id/text_title"
android:layout_width="150dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/icon" android:layout_below="@id/icon"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:paddingStart="15dp" android:paddingStart="15dp"
android:paddingTop="15dp" android:paddingTop="10dp"
android:paddingEnd="15dp" android:paddingEnd="15dp"
android:singleLine="true"
android:textAlignment="center" android:textAlignment="center"
android:textAppearance="?android:attr/textAppearanceListItem" android:textAppearance="?android:attr/textAppearanceListItem"
tools:ignore="RelativeOverlap" /> tools:ignore="RelativeOverlap" />
<TextView <TextView
android:id="@+id/text_subtitle" android:id="@+id/text_subtitle"
android:layout_width="150dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/text_title" android:layout_below="@id/text_title"
android:layout_alignStart="@id/text_title" android:layout_alignStart="@id/text_title"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:marqueeRepeatLimit="marquee_forever"
android:paddingBottom="15dp" android:paddingBottom="15dp"
android:singleLine="true"
android:textAlignment="center" android:textAlignment="center"
android:textAppearance="?android:attr/textAppearanceListItemSecondary" android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="@android:color/tertiary_text_light" /> android:textColor="@android:color/tertiary_text_light" />

View File

@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:keepScreenOn="true"
tools:context=".EmulationActivity"> tools:context=".EmulationActivity">
<SurfaceView <SurfaceView

View File

@ -28,7 +28,6 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
android:padding="8dp"
app:maxImageSize="26dp" app:maxImageSize="26dp"
app:srcCompat="@drawable/ic_open" /> app:srcCompat="@drawable/ic_open" />
@ -37,7 +36,6 @@
style="@style/Widget.MaterialComponents.FloatingActionButton" style="@style/Widget.MaterialComponents.FloatingActionButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="8dp"
app:maxImageSize="26dp" app:maxImageSize="26dp"
app:srcCompat="@drawable/ic_log" /> app:srcCompat="@drawable/ic_log" />
</LinearLayout> </LinearLayout>

View File

@ -1,8 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.appbar.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android" <com.google.android.material.appbar.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/toolbar_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:descendantFocusability="afterDescendants"
app:liftOnScroll="true"> app:liftOnScroll="true">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar