Android 12 Support + Update Libraries + Include Khronos Validation Layer

* Fix handling `SA_EXPOSE_TAGBITS` bit being set in Android 12 `sigaction`
* Fix CMake bug using `CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE` when not supported causing `-fuse-ld=gold` to be emitted as a linker flag
* Support using `VIBRATOR_MANAGER_SERVICE` rather than `VIBRATOR_SERVICE` on Android 12
* Optimize Imports for Kotlin code
* Move away from deprecated APIs in Kotlin or explicitly mark where it's not possible
* Update SDK, NDK and libraries
* Enable Gradle Configuration Cache
This commit is contained in:
PixelyIon 2021-10-26 10:45:49 +05:30
parent b7d0f2fafa
commit a7548c79a0
27 changed files with 116 additions and 122 deletions

View File

@ -11,7 +11,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
set(source_DIR ${CMAKE_SOURCE_DIR}/src/main/cpp) set(source_DIR ${CMAKE_SOURCE_DIR}/src/main/cpp)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing")
set(CMAKE_CXX_FLAGS_RELEASE "-Ofast -flto=full -fno-stack-protector -Wno-unused-command-line-argument -DNDEBUG") set(CMAKE_CXX_FLAGS_RELEASE "-Ofast -flto=full -fno-stack-protector -Wno-unused-command-line-argument -DNDEBUG")
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE)
# {fmt} # {fmt}
add_subdirectory("libraries/fmt") add_subdirectory("libraries/fmt")

View File

@ -6,13 +6,13 @@ plugins {
} }
android { android {
compileSdkVersion 30 compileSdkVersion 31
buildToolsVersion '30.0.3' buildToolsVersion '31.0.0'
defaultConfig { defaultConfig {
applicationId "skyline.emu" applicationId "skyline.emu"
minSdkVersion 29 minSdkVersion 29
targetSdkVersion 30 targetSdkVersion 31
versionCode 3 versionCode 3
versionName "0.0.3" versionName "0.0.3"
@ -50,19 +50,10 @@ android {
debuggable true debuggable true
minifyEnabled false minifyEnabled false
shrinkResources false shrinkResources false
/* Vulkan Validation Layers */
sourceSets {
main {
jniLibs {
srcDir "$buildDir/generated/vulkan_layers"
}
}
}
} }
} }
buildFeatures { buildFeatures {
prefab true
viewBinding true viewBinding true
} }
@ -72,7 +63,7 @@ android {
} }
/* NDK */ /* NDK */
ndkVersion '22.1.7171670' ndkVersion '23.0.7599858'
externalNativeBuild { externalNativeBuild {
cmake { cmake {
version '3.18.1+' version '3.18.1+'
@ -84,41 +75,29 @@ android {
aaptOptions { aaptOptions {
ignoreAssetsPattern "*.md" ignoreAssetsPattern "*.md"
} }
}
/** /* Vulkan Validation Layers */
* We just want VK_LAYER_KHRONOS_validation in the APK while NDK contains several other legacy layers sourceSets {
* This task copies shared objects associated with that layer into a folder from where JNI can use it debug {
*/ jniLibs {
task setupValidationLayer(type: Copy) { srcDir "libraries/vklayers"
doFirst { }
def folder = new File("$buildDir/generated/vulkan_layers")
if (!folder.exists()) {
folder.mkdirs() // We need to recursively create all directories as the copy requires the output directory to exist
} }
} }
from("${android.ndkDirectory}/sources/third_party/vulkan/src/build-android/jniLibs") {
include "*/libVkLayer_khronos_validation.so"
}
into "$buildDir/generated/vulkan_layers"
}
afterEvaluate {
preDebugBuild.dependsOn setupValidationLayer
} }
dependencies { dependencies {
/* Google */ /* Google */
implementation "androidx.core:core-ktx:1.6.0" implementation "androidx.core:core-ktx:1.6.0"
implementation 'androidx.appcompat:appcompat:1.3.0' implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
implementation 'androidx.preference:preference-ktx:1.1.1' implementation 'androidx.preference:preference-ktx:1.1.1'
implementation 'com.google.android.material:material:1.4.0' implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.documentfile:documentfile:1.0.1' implementation 'androidx.documentfile:documentfile:1.0.1'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
implementation 'androidx.fragment:fragment-ktx:1.3.5' implementation 'androidx.fragment:fragment-ktx:1.3.6'
implementation "com.google.dagger:hilt-android:$hilt_version" implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-android-compiler:$hilt_version" kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
implementation 'com.google.android:flexbox:2.0.1' implementation 'com.google.android:flexbox:2.0.1'
@ -129,3 +108,7 @@ dependencies {
/* Other Java */ /* Other Java */
implementation 'info.debatty:java-string-similarity:2.0.0' implementation 'info.debatty:java-string-similarity:2.0.0'
} }
kapt {
correctErrorTypes true
}

@ -1 +1 @@
Subproject commit 4237966924d69ea5c875d5c2072fc0804f15b4b5 Subproject commit e32407f2b4cac7f0d739f10821d7f7e0c81e8436

@ -1 +1 @@
Subproject commit 9e8b86fd2d9806672cc73133d21780dd182bfd24 Subproject commit dcd282bb268a0766f6d273b6e41a3a96719bbbfb

@ -1 +1 @@
Subproject commit 96ac3481a9e9543f44b4de8e65af9ad2b040241c Subproject commit 7264ab0eae2a072afa55d617d5e9e11bcb464aae

@ -1 +1 @@
Subproject commit f126f79fd3d9cbfac78f6e8d8e8bc1cd9f4db849 Subproject commit 855ea841a93bf304065e5152909983b1b85ffabb

@ -1 +1 @@
Subproject commit 08b3433180727ea2f78fe02e860a08471db1e03c Subproject commit 9e382f98076e57581fcc61323728443374889646

@ -1 +1 @@
Subproject commit 8c7a248a72c685ce28cbc63f6c40e6a2ea786346 Subproject commit 4bcd301070d7d4a3d6ca191da96430025d708a6a

@ -1 +1 @@
Subproject commit 0790b5f0a9b96cd79fe75e4f458fc6b468dd9ec3 Subproject commit 895b080a3c2189feaea0919af8982e9a248ff7d6

View File

@ -22,12 +22,15 @@
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning,UnusedAttribute"> tools:ignore="GoogleAppIndexingWarning,UnusedAttribute">
<activity android:name="emu.skyline.MainActivity"> <activity
android:name="emu.skyline.MainActivity"
android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name="emu.skyline.SettingsActivity" android:name="emu.skyline.SettingsActivity"
android:exported="true" android:exported="true"
@ -37,6 +40,7 @@
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="emu.skyline.MainActivity" /> android:value="emu.skyline.MainActivity" />
</activity> </activity>
<activity <activity
android:name="emu.skyline.input.ControllerActivity" android:name="emu.skyline.input.ControllerActivity"
android:exported="true"> android:exported="true">
@ -44,6 +48,7 @@
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="emu.skyline.SettingsActivity" /> android:value="emu.skyline.SettingsActivity" />
</activity> </activity>
<activity <activity
android:name="emu.skyline.input.onscreen.OnScreenEditActivity" android:name="emu.skyline.input.onscreen.OnScreenEditActivity"
android:exported="true" android:exported="true"
@ -53,9 +58,11 @@
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="emu.skyline.input.ControllerActivity" /> android:value="emu.skyline.input.ControllerActivity" />
</activity> </activity>
<activity <activity
android:name="emu.skyline.EmulationActivity" android:name="emu.skyline.EmulationActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:exported="true"
android:launchMode="singleInstance" android:launchMode="singleInstance"
android:screenOrientation="landscape" android:screenOrientation="landscape"
tools:ignore="LockedOrientationActivity"> tools:ignore="LockedOrientationActivity">

View File

@ -149,7 +149,7 @@ namespace skyline {
*/ */
template<typename S, typename... Args> template<typename S, typename... Args>
auto Format(S formatString, Args &&... args) { auto Format(S formatString, Args &&... args) {
return fmt::format(formatString, FmtCast(args)...); return fmt::format(fmt::runtime(formatString), FmtCast(args)...);
} }
} }
@ -159,7 +159,7 @@ namespace skyline {
class exception : public std::runtime_error { class exception : public std::runtime_error {
public: public:
template<typename S, typename... Args> template<typename S, typename... Args>
exception(const S &formatStr, Args &&... args) : runtime_error(fmt::format(formatStr, util::FmtCast(args)...)) {} exception(const S &formatStr, Args &&... args) : runtime_error(fmt::format(fmt::runtime(formatStr), util::FmtCast(args)...)) {}
}; };
namespace util { namespace util {
@ -364,7 +364,6 @@ namespace skyline {
template<typename T> requires (std::is_integral_v<T>) template<typename T> requires (std::is_integral_v<T>)
void FillRandomBytes(std::span<T> in) { void FillRandomBytes(std::span<T> in) {
std::independent_bits_engine<std::mt19937_64, std::numeric_limits<T>::digits, T> gen(detail::generator); std::independent_bits_engine<std::mt19937_64, std::numeric_limits<T>::digits, T> gen(detail::generator);
std::generate(in.begin(), in.end(), gen); std::generate(in.begin(), in.end(), gen);
} }
@ -577,7 +576,7 @@ namespace skyline {
S string; S string;
const char *function; const char *function;
FunctionString(S string, const char *function = __builtin_FUNCTION()) : string(std::move(string)), function(function) {} constexpr FunctionString(S string, const char *function = __builtin_FUNCTION()) : string(std::move(string)), function(function) {}
std::string operator*() { std::string operator*() {
return std::string(function) + ": " + std::string(string); return std::string(function) + ": " + std::string(string);
@ -587,91 +586,91 @@ namespace skyline {
template<typename... Args> template<typename... Args>
void Error(FunctionString<const char*> formatString, Args &&... args) { void Error(FunctionString<const char*> formatString, Args &&... args) {
if (LogLevel::Error <= configLevel) if (LogLevel::Error <= configLevel)
Write(LogLevel::Error, fmt::format(*formatString, util::FmtCast(args)...)); Write(LogLevel::Error, fmt::format(fmt::runtime(*formatString), util::FmtCast(args)...));
} }
template<typename... Args> template<typename... Args>
void Error(FunctionString<std::string> formatString, Args &&... args) { void Error(FunctionString<std::string> formatString, Args &&... args) {
if (LogLevel::Error <= configLevel) if (LogLevel::Error <= configLevel)
Write(LogLevel::Error, fmt::format(*formatString, util::FmtCast(args)...)); Write(LogLevel::Error, fmt::format(fmt::runtime(*formatString), util::FmtCast(args)...));
} }
template<typename S, typename... Args> template<typename S, typename... Args>
void ErrorNoPrefix(S formatString, Args &&... args) { void ErrorNoPrefix(S formatString, Args &&... args) {
if (LogLevel::Error <= configLevel) if (LogLevel::Error <= configLevel)
Write(LogLevel::Error, fmt::format(formatString, util::FmtCast(args)...)); Write(LogLevel::Error, fmt::format(fmt::runtime(formatString), util::FmtCast(args)...));
} }
template<typename... Args> template<typename... Args>
void Warn(FunctionString<const char*> formatString, Args &&... args) { void Warn(FunctionString<const char*> formatString, Args &&... args) {
if (LogLevel::Warn <= configLevel) if (LogLevel::Warn <= configLevel)
Write(LogLevel::Warn, fmt::format(*formatString, util::FmtCast(args)...)); Write(LogLevel::Warn, fmt::format(fmt::runtime(*formatString), util::FmtCast(args)...));
} }
template<typename... Args> template<typename... Args>
void Warn(FunctionString<std::string> formatString, Args &&... args) { void Warn(FunctionString<std::string> formatString, Args &&... args) {
if (LogLevel::Warn <= configLevel) if (LogLevel::Warn <= configLevel)
Write(LogLevel::Warn, fmt::format(*formatString, util::FmtCast(args)...)); Write(LogLevel::Warn, fmt::format(fmt::runtime(*formatString), util::FmtCast(args)...));
} }
template<typename S, typename... Args> template<typename S, typename... Args>
void WarnNoPrefix(S formatString, Args &&... args) { void WarnNoPrefix(S formatString, Args &&... args) {
if (LogLevel::Warn <= configLevel) if (LogLevel::Warn <= configLevel)
Write(LogLevel::Warn, fmt::format(formatString, util::FmtCast(args)...)); Write(LogLevel::Warn, fmt::format(fmt::runtime(formatString), util::FmtCast(args)...));
} }
template<typename... Args> template<typename... Args>
void Info(FunctionString<const char*> formatString, Args &&... args) { void Info(FunctionString<const char*> formatString, Args &&... args) {
if (LogLevel::Info <= configLevel) if (LogLevel::Info <= configLevel)
Write(LogLevel::Info, fmt::format(*formatString, util::FmtCast(args)...)); Write(LogLevel::Info, fmt::format(fmt::runtime(*formatString), util::FmtCast(args)...));
} }
template<typename... Args> template<typename... Args>
void Info(FunctionString<std::string> formatString, Args &&... args) { void Info(FunctionString<std::string> formatString, Args &&... args) {
if (LogLevel::Info <= configLevel) if (LogLevel::Info <= configLevel)
Write(LogLevel::Info, fmt::format(*formatString, util::FmtCast(args)...)); Write(LogLevel::Info, fmt::format(fmt::runtime(*formatString), util::FmtCast(args)...));
} }
template<typename S, typename... Args> template<typename S, typename... Args>
void InfoNoPrefix(S formatString, Args &&... args) { void InfoNoPrefix(S formatString, Args &&... args) {
if (LogLevel::Info <= configLevel) if (LogLevel::Info <= configLevel)
Write(LogLevel::Info, fmt::format(formatString, util::FmtCast(args)...)); Write(LogLevel::Info, fmt::format(fmt::runtime(formatString), util::FmtCast(args)...));
} }
template<typename... Args> template<typename... Args>
void Debug(FunctionString<const char*> formatString, Args &&... args) { void Debug(FunctionString<const char*> formatString, Args &&... args) {
if (LogLevel::Debug <= configLevel) if (LogLevel::Debug <= configLevel)
Write(LogLevel::Debug, fmt::format(*formatString, util::FmtCast(args)...)); Write(LogLevel::Debug, fmt::format(fmt::runtime(*formatString), util::FmtCast(args)...));
} }
template<typename... Args> template<typename... Args>
void Debug(FunctionString<std::string> formatString, Args &&... args) { void Debug(FunctionString<std::string> formatString, Args &&... args) {
if (LogLevel::Debug <= configLevel) if (LogLevel::Debug <= configLevel)
Write(LogLevel::Debug, fmt::format(*formatString, util::FmtCast(args)...)); Write(LogLevel::Debug, fmt::format(fmt::runtime(*formatString), util::FmtCast(args)...));
} }
template<typename S, typename... Args> template<typename S, typename... Args>
void DebugNoPrefix(S formatString, Args &&... args) { void DebugNoPrefix(S formatString, Args &&... args) {
if (LogLevel::Debug <= configLevel) if (LogLevel::Debug <= configLevel)
Write(LogLevel::Debug, fmt::format(formatString, util::FmtCast(args)...)); Write(LogLevel::Debug, fmt::format(fmt::runtime(formatString), util::FmtCast(args)...));
} }
template<typename... Args> template<typename... Args>
void Verbose(FunctionString<const char*> formatString, Args &&... args) { void Verbose(FunctionString<const char*> formatString, Args &&... args) {
if (LogLevel::Verbose <= configLevel) if (LogLevel::Verbose <= configLevel)
Write(LogLevel::Verbose, fmt::format(*formatString, util::FmtCast(args)...)); Write(LogLevel::Verbose, fmt::format(fmt::runtime(*formatString), util::FmtCast(args)...));
} }
template<typename... Args> template<typename... Args>
void Verbose(FunctionString<std::string> formatString, Args &&... args) { void Verbose(FunctionString<std::string> formatString, Args &&... args) {
if (LogLevel::Verbose <= configLevel) if (LogLevel::Verbose <= configLevel)
Write(LogLevel::Verbose, fmt::format(*formatString, util::FmtCast(args)...)); Write(LogLevel::Verbose, fmt::format(fmt::runtime(*formatString), util::FmtCast(args)...));
} }
template<typename S, typename... Args> template<typename S, typename... Args>
void VerboseNoPrefix(S formatString, Args &&... args) { void VerboseNoPrefix(S formatString, Args &&... args) {
if (LogLevel::Verbose <= configLevel) if (LogLevel::Verbose <= configLevel)
Write(LogLevel::Verbose, fmt::format(formatString, util::FmtCast(args)...)); Write(LogLevel::Verbose, fmt::format(fmt::runtime(formatString), util::FmtCast(args)...));
} }
}; };

View File

@ -4,6 +4,7 @@
#include <unistd.h> #include <unistd.h>
#include <dlfcn.h> #include <dlfcn.h>
#include <unwind.h> #include <unwind.h>
#include <fcntl.h>
#include "signal.h" #include "signal.h"
namespace skyline::signal { namespace skyline::signal {
@ -172,19 +173,21 @@ namespace skyline::signal {
void SetSignalHandler(std::initializer_list<int> signals, SignalHandler function, bool syscallRestart) { void SetSignalHandler(std::initializer_list<int> signals, SignalHandler function, bool syscallRestart) {
static std::array<std::once_flag, NSIG> signalHandlerOnce{}; static std::array<std::once_flag, NSIG> signalHandlerOnce{};
stack_t stack;
sigaltstack(nullptr, &stack);
struct sigaction action{ struct sigaction action{
.sa_sigaction = reinterpret_cast<void (*)(int, siginfo *, void *)>(ThreadSignalHandler), .sa_sigaction = reinterpret_cast<void (*)(int, siginfo *, void *)>(ThreadSignalHandler),
.sa_flags = (syscallRestart ? SA_RESTART : 0) | SA_SIGINFO | (stack.ss_sp && stack.ss_size ? SA_ONSTACK : 0), .sa_flags = SA_SIGINFO | SA_EXPOSE_TAGBITS | (syscallRestart ? SA_RESTART : 0) | SA_ONSTACK,
}; };
for (int signal : signals) { for (int signal : signals) {
std::call_once(signalHandlerOnce[signal], [signal, &action]() { std::call_once(signalHandlerOnce[signal], [signal, &action]() {
struct sigaction oldAction; struct sigaction oldAction;
Sigaction(signal, &action, &oldAction); Sigaction(signal, &action, &oldAction);
if (oldAction.sa_flags && oldAction.sa_flags != action.sa_flags) if (oldAction.sa_flags) {
throw exception("Old sigaction flags aren't equivalent to the replaced signal: {:#b} | {:#b}", oldAction.sa_flags, action.sa_flags); oldAction.sa_flags &= ~SA_UNSUPPORTED; // Mask out kernel not supporting old sigaction() bits
oldAction.sa_flags |= SA_SIGINFO | SA_EXPOSE_TAGBITS | SA_RESTART | SA_ONSTACK; // Intentionally ignore these flags for the comparison
if (oldAction.sa_flags != (action.sa_flags | SA_RESTART))
throw exception("Old sigaction flags aren't equivalent to the replaced signal: {:#b} | {:#b}", oldAction.sa_flags, action.sa_flags);
}
DefaultSignalHandlers.at(signal).function = (oldAction.sa_flags & SA_SIGINFO) ? oldAction.sa_sigaction : reinterpret_cast<void (*)(int, struct siginfo *, void *)>(oldAction.sa_handler); DefaultSignalHandlers.at(signal).function = (oldAction.sa_flags & SA_SIGINFO) ? oldAction.sa_sigaction : reinterpret_cast<void (*)(int, struct siginfo *, void *)>(oldAction.sa_handler);
}); });

View File

@ -10,7 +10,6 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.res.AssetManager import android.content.res.AssetManager
import android.graphics.PointF import android.graphics.PointF
import android.net.Uri
import android.os.* import android.os.*
import android.util.Log import android.util.Log
import android.view.* import android.view.*
@ -434,15 +433,25 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
} else { } else {
inputManager.controllers[index]!!.rumbleDeviceDescriptor?.let { inputManager.controllers[index]!!.rumbleDeviceDescriptor?.let {
if (it == "builtin") { if (it == "builtin") {
val vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator val vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val vibratorManager = getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
vibratorManager.defaultVibrator
} else {
@Suppress("DEPRECATION")
getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
}
vibrators[index] = vibrator vibrators[index] = vibrator
vibrator vibrator
} else { } else {
for (id in InputDevice.getDeviceIds()) { for (id in InputDevice.getDeviceIds()) {
val device = InputDevice.getDevice(id) val device = InputDevice.getDevice(id)
if (device.descriptor == inputManager.controllers[index]!!.rumbleDeviceDescriptor) { if (device.descriptor == inputManager.controllers[index]!!.rumbleDeviceDescriptor) {
vibrators[index] = device.vibrator vibrators[index] = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
device.vibrator device.vibratorManager.defaultVibrator
} else {
@Suppress("DEPRECATION")
device.vibrator
}
} }
} }
} }

View File

@ -36,10 +36,9 @@ import emu.skyline.loader.AppEntry
import emu.skyline.loader.LoaderResult import emu.skyline.loader.LoaderResult
import emu.skyline.loader.RomFormat import emu.skyline.loader.RomFormat
import emu.skyline.utils.Settings import emu.skyline.utils.Settings
import emu.skyline.utils.toFile
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.ceil import kotlin.math.ceil
import kotlin.math.roundToInt
@AndroidEntryPoint @AndroidEntryPoint
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {

View File

@ -23,7 +23,7 @@ class RomProvider @Inject constructor(@ApplicationContext private val context :
if (file.isDirectory) { if (file.isDirectory) {
addEntries(fileFormats, file, entries, systemLanguage) addEntries(fileFormats, file, entries, systemLanguage)
} else { } else {
fileFormats[file.name?.substringAfterLast(".")?.toLowerCase()]?.let { romFormat-> fileFormats[file.name?.substringAfterLast(".")?.lowercase()]?.let { romFormat->
entries.getOrPut(romFormat, { arrayListOf() }).add(RomFile(context, romFormat, file.uri, systemLanguage).appEntry) entries.getOrPut(romFormat, { arrayListOf() }).add(RomFile(context, romFormat, file.uri, systemLanguage).appEntry)
} }
} }

View File

@ -64,9 +64,10 @@ class SettingsActivity : AppCompatActivity() {
if (parentFragmentManager.findFragmentByTag(DIALOG_FRAGMENT_TAG) != null) if (parentFragmentManager.findFragmentByTag(DIALOG_FRAGMENT_TAG) != null)
return return
val f = IntegerListPreference.IntegerListPreferenceDialogFragmentCompat.newInstance(preference.getKey()) val dialogFragment = IntegerListPreference.IntegerListPreferenceDialogFragmentCompat.newInstance(preference.getKey())
f.setTargetFragment(this, 0) @Suppress("DEPRECATION")
f.show(parentFragmentManager, DIALOG_FRAGMENT_TAG) dialogFragment.setTargetFragment(this, 0) // androidx.preference.PreferenceDialogFragmentCompat depends on the target fragment being set correctly even though it's deprecated
dialogFragment.show(parentFragmentManager, DIALOG_FRAGMENT_TAG)
} else { } else {
super.onDisplayPreferenceDialog(preference) super.onDisplayPreferenceDialog(preference)
} }

View File

@ -90,7 +90,7 @@ class GenericAdapter : RecyclerView.Adapter<GenericViewHolder<ViewBinding>>(), F
* This sorts the items in [allItems] in relation to how similar they are to [currentSearchTerm] * This sorts the items in [allItems] in relation to how similar they are to [currentSearchTerm]
*/ */
fun extractSorted() = allItems.mapNotNull { item -> fun extractSorted() = allItems.mapNotNull { item ->
item.key().toLowerCase(Locale.getDefault()).let { item.key().lowercase(Locale.getDefault()).let {
val similarity = (jw.similarity(currentSearchTerm, it) + cos.similarity(currentSearchTerm, it)) / 2 val similarity = (jw.similarity(currentSearchTerm, it) + cos.similarity(currentSearchTerm, it)) / 2
if (similarity != 0.0) ScoredItem(similarity, item) else null if (similarity != 0.0) ScoredItem(similarity, item) else null
} }
@ -99,7 +99,7 @@ class GenericAdapter : RecyclerView.Adapter<GenericViewHolder<ViewBinding>>(), F
/** /**
* This performs filtering on the items in [allItems] based on similarity to [term] * This performs filtering on the items in [allItems] based on similarity to [term]
*/ */
override fun performFiltering(term : CharSequence) = (term as String).toLowerCase(Locale.getDefault()).let { lowerCaseTerm -> override fun performFiltering(term : CharSequence) = (term as String).lowercase(Locale.getDefault()).let { lowerCaseTerm ->
currentSearchTerm = lowerCaseTerm currentSearchTerm = lowerCaseTerm
with(if (term.isEmpty()) { with(if (term.isEmpty()) {
@ -108,7 +108,7 @@ class GenericAdapter : RecyclerView.Adapter<GenericViewHolder<ViewBinding>>(), F
val filterData = mutableListOf<GenericListItem<*>>() val filterData = mutableListOf<GenericListItem<*>>()
val topResults = extractSorted() val topResults = extractSorted()
val avgScore = topResults.sumByDouble { it.score } / topResults.size val avgScore = topResults.sumOf { it.score } / topResults.size
for (result in topResults) for (result in topResults)
if (result.score >= avgScore) filterData.add(result.item) if (result.score >= avgScore) filterData.add(result.item)
@ -131,4 +131,4 @@ class GenericAdapter : RecyclerView.Adapter<GenericViewHolder<ViewBinding>>(), F
onFilterPublishedListener?.invoke() onFilterPublishedListener?.invoke()
} }
} }
} }

View File

@ -8,7 +8,6 @@ package emu.skyline.adapter.controller
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isGone import androidx.core.view.isGone
import emu.skyline.adapter.GenericListItem import emu.skyline.adapter.GenericListItem
import emu.skyline.adapter.GenericViewHolder
import emu.skyline.adapter.ViewBindingFactory import emu.skyline.adapter.ViewBindingFactory
import emu.skyline.adapter.inflater import emu.skyline.adapter.inflater
import emu.skyline.databinding.ControllerCheckboxItemBinding import emu.skyline.databinding.ControllerCheckboxItemBinding

View File

@ -8,11 +8,9 @@ package emu.skyline.adapter.controller
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isGone import androidx.core.view.isGone
import emu.skyline.adapter.GenericListItem import emu.skyline.adapter.GenericListItem
import emu.skyline.adapter.GenericViewHolder
import emu.skyline.adapter.ViewBindingFactory import emu.skyline.adapter.ViewBindingFactory
import emu.skyline.adapter.inflater import emu.skyline.adapter.inflater
import emu.skyline.databinding.ControllerItemBinding import emu.skyline.databinding.ControllerItemBinding
import emu.skyline.input.InputManager
object ControllerBindingFactory : ViewBindingFactory { object ControllerBindingFactory : ViewBindingFactory {
override fun createBinding(parent : ViewGroup) = ControllerItemBinding.inflate(parent.inflater(), parent, false) override fun createBinding(parent : ViewGroup) = ControllerItemBinding.inflate(parent.inflater(), parent, false)

View File

@ -7,9 +7,7 @@ package emu.skyline.input.dialog
import android.animation.LayoutTransition import android.animation.LayoutTransition
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.*
import android.os.VibrationEffect
import android.os.Vibrator
import android.view.* import android.view.*
import android.view.animation.LinearInterpolator import android.view.animation.LinearInterpolator
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior
@ -64,7 +62,15 @@ class RumbleDialog @JvmOverloads constructor(val item : ControllerGeneralViewIte
if (context.id == 0) { if (context.id == 0) {
binding.rumbleBuiltin.visibility = View.VISIBLE binding.rumbleBuiltin.visibility = View.VISIBLE
if (!(context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator).hasVibrator()) val vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val vibratorManager = context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
vibratorManager.defaultVibrator
} else {
@Suppress("DEPRECATION")
context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
}
if (!vibrator.hasVibrator())
binding.rumbleBuiltin.isEnabled = false binding.rumbleBuiltin.isEnabled = false
binding.rumbleBuiltin.setOnClickListener { binding.rumbleBuiltin.setOnClickListener {
controller.rumbleDeviceDescriptor = "builtin" controller.rumbleDeviceDescriptor = "builtin"
@ -85,7 +91,12 @@ class RumbleDialog @JvmOverloads constructor(val item : ControllerGeneralViewIte
// We want all input events from Joysticks and game pads // We want all input events from Joysticks and game pads
if (event.isFromSource(InputDevice.SOURCE_GAMEPAD) || event.isFromSource(InputDevice.SOURCE_JOYSTICK)) { if (event.isFromSource(InputDevice.SOURCE_GAMEPAD) || event.isFromSource(InputDevice.SOURCE_JOYSTICK)) {
if (event.repeatCount == 0 && event.action == KeyEvent.ACTION_DOWN) { if (event.repeatCount == 0 && event.action == KeyEvent.ACTION_DOWN) {
val vibrator = event.device.vibrator val vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
event.device.vibratorManager.defaultVibrator
} else {
@Suppress("DEPRECATION")
event.device.vibrator
}
when { when {
// If the device doesn't match the currently selected device then update the UI accordingly and set [deviceId] to the current device // If the device doesn't match the currently selected device then update the UI accordingly and set [deviceId] to the current device

View File

@ -41,7 +41,7 @@ fun getRomFormat(uri : Uri, contentResolver : ContentResolver) : RomFormat {
cursor.moveToFirst() cursor.moveToFirst()
uriStr = cursor.getString(nameIndex) uriStr = cursor.getString(nameIndex)
} }
return RomFormat.valueOf(uriStr.substring(uriStr.lastIndexOf(".") + 1).toUpperCase(Locale.ROOT)) return RomFormat.valueOf(uriStr.substring(uriStr.lastIndexOf(".") + 1).uppercase(Locale.ROOT))
} }
/** /**
@ -150,4 +150,4 @@ internal class RomFile(context : Context, format : RomFormat, uri : Uri, systemL
* @return A pointer to the newly allocated object, or 0 if the ROM is invalid * @return A pointer to the newly allocated object, or 0 if the ROM is invalid
*/ */
private external fun populate(format : Int, romFd : Int, appFilesPath : String, systemLanguage : Int) : Int private external fun populate(format : Int, romFd : Int, appFilesPath : String, systemLanguage : Int) : Int
} }

View File

@ -5,24 +5,23 @@
package emu.skyline.preference package emu.skyline.preference
import android.annotation.SuppressLint
import android.content.Context
import android.content.DialogInterface
import android.content.res.Resources
import android.content.res.TypedArray
import android.os.Bundle import android.os.Bundle
import android.os.Parcel import android.os.Parcel
import android.os.Parcelable import android.os.Parcelable
import android.os.Parcelable.Creator
import android.util.AttributeSet import android.util.AttributeSet
import android.content.Context
import android.content.res.Resources
import android.content.res.TypedArray
import android.content.DialogInterface
import android.annotation.SuppressLint
import androidx.annotation.ArrayRes import androidx.annotation.ArrayRes
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.content.res.TypedArrayUtils import androidx.core.content.res.TypedArrayUtils
import androidx.preference.R
import androidx.preference.DialogPreference import androidx.preference.DialogPreference
import androidx.preference.PreferenceDialogFragmentCompat import androidx.preference.PreferenceDialogFragmentCompat
import emu.skyline.R as sR import androidx.preference.R
import emu.skyline.di.getSettings import emu.skyline.di.getSettings
import emu.skyline.R as sR
/** /**
* A Preference that displays a list of strings in a dialog and saves an integer that corresponds to the selected entry (as specified by entryValues or the index of the selected entry) * A Preference that displays a list of strings in a dialog and saves an integer that corresponds to the selected entry (as specified by entryValues or the index of the selected entry)
@ -177,19 +176,6 @@ class IntegerListPreference @JvmOverloads constructor(
super.writeToParcel(dest, flags) super.writeToParcel(dest, flags)
dest.writeSerializable(value) dest.writeSerializable(value)
} }
companion object {
@JvmField
val CREATOR : Creator<SavedState> = object : Creator<SavedState> {
override fun createFromParcel(input : Parcel) : SavedState? {
return SavedState(input)
}
override fun newArray(size : Int) : Array<SavedState?> {
return arrayOfNulls(size)
}
}
}
} }
/** /**
@ -286,4 +272,4 @@ class IntegerListPreference @JvmOverloads constructor(
} }
} }
} }
} }

View File

@ -44,7 +44,7 @@ class SharedPreferencesDelegate<T>(context : Context, private val clazz : Class<
private fun camelToSnakeCase(text : String) = StringBuilder().apply { private fun camelToSnakeCase(text : String) = StringBuilder().apply {
text.forEachIndexed { index, c -> text.forEachIndexed { index, c ->
if (index != 0 && c.isUpperCase()) append('_') if (index != 0 && c.isUpperCase()) append('_')
append(c.toLowerCase()) append(c.lowercase())
}.toString() }.toString()
} }
} }

View File

@ -2,19 +2,18 @@
buildscript { buildscript {
ext { ext {
kotlin_version = '1.4.30' kotlin_version = '1.5.31'
lifecycle_version = '2.3.1' lifecycle_version = '2.3.1'
hilt_version = '2.33-beta' hilt_version = '2.39.1'
lifecycle_version = '2.3.1' lifecycle_version = '2.3.1'
} }
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:4.1.2' classpath 'com.android.tools.build:gradle:7.0.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version" classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
@ -28,7 +27,6 @@ allprojects {
google() google()
mavenCentral() mavenCentral()
maven { url "https://google.bintray.com/flexbox-layout" } maven { url "https://google.bintray.com/flexbox-layout" }
jcenter()
} }
} }

View File

@ -21,3 +21,5 @@ android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
# Android NDK verbose build output # Android NDK verbose build output
android.native.buildOutput=verbose android.native.buildOutput=verbose
# Gradle Configuration Cache
org.gradle.unsafe.configuration-cache=true

View File

@ -1,6 +1,6 @@
#Mon Feb 08 07:42:45 GMT 2021 #Mon Oct 11 06:23:28 GMT 2021
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-milestone-1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME