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(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_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE)
# {fmt}
add_subdirectory("libraries/fmt")

View File

@ -6,13 +6,13 @@ plugins {
}
android {
compileSdkVersion 30
buildToolsVersion '30.0.3'
compileSdkVersion 31
buildToolsVersion '31.0.0'
defaultConfig {
applicationId "skyline.emu"
minSdkVersion 29
targetSdkVersion 30
targetSdkVersion 31
versionCode 3
versionName "0.0.3"
@ -50,19 +50,10 @@ android {
debuggable true
minifyEnabled false
shrinkResources false
}
}
/* Vulkan Validation Layers */
sourceSets {
main {
jniLibs {
srcDir "$buildDir/generated/vulkan_layers"
}
}
}
}
}
buildFeatures {
prefab true
viewBinding true
}
@ -72,7 +63,7 @@ android {
}
/* NDK */
ndkVersion '22.1.7171670'
ndkVersion '23.0.7599858'
externalNativeBuild {
cmake {
version '3.18.1+'
@ -84,41 +75,29 @@ android {
aaptOptions {
ignoreAssetsPattern "*.md"
}
}
/**
* We just want VK_LAYER_KHRONOS_validation in the APK while NDK contains several other legacy layers
* This task copies shared objects associated with that layer into a folder from where JNI can use it
*/
task setupValidationLayer(type: Copy) {
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
/* Vulkan Validation Layers */
sourceSets {
debug {
jniLibs {
srcDir "libraries/vklayers"
}
}
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 {
/* Google */
implementation "androidx.core:core-ktx:1.6.0"
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
implementation 'androidx.preference:preference-ktx:1.1.1'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.documentfile:documentfile:1.0.1'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation "androidx.lifecycle:lifecycle-viewmodel-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"
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
implementation 'com.google.android:flexbox:2.0.1'
@ -129,3 +108,7 @@ dependencies {
/* Other Java */
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:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning,UnusedAttribute">
<activity android:name="emu.skyline.MainActivity">
<activity
android:name="emu.skyline.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="emu.skyline.SettingsActivity"
android:exported="true"
@ -37,6 +40,7 @@
android:name="android.support.PARENT_ACTIVITY"
android:value="emu.skyline.MainActivity" />
</activity>
<activity
android:name="emu.skyline.input.ControllerActivity"
android:exported="true">
@ -44,6 +48,7 @@
android:name="android.support.PARENT_ACTIVITY"
android:value="emu.skyline.SettingsActivity" />
</activity>
<activity
android:name="emu.skyline.input.onscreen.OnScreenEditActivity"
android:exported="true"
@ -53,9 +58,11 @@
android:name="android.support.PARENT_ACTIVITY"
android:value="emu.skyline.input.ControllerActivity" />
</activity>
<activity
android:name="emu.skyline.EmulationActivity"
android:configChanges="orientation|screenSize"
android:exported="true"
android:launchMode="singleInstance"
android:screenOrientation="landscape"
tools:ignore="LockedOrientationActivity">

View File

@ -149,7 +149,7 @@ namespace skyline {
*/
template<typename S, typename... 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 {
public:
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 {
@ -364,7 +364,6 @@ namespace skyline {
template<typename T> requires (std::is_integral_v<T>)
void FillRandomBytes(std::span<T> in) {
std::independent_bits_engine<std::mt19937_64, std::numeric_limits<T>::digits, T> gen(detail::generator);
std::generate(in.begin(), in.end(), gen);
}
@ -577,7 +576,7 @@ namespace skyline {
S string;
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*() {
return std::string(function) + ": " + std::string(string);
@ -587,91 +586,91 @@ namespace skyline {
template<typename... Args>
void Error(FunctionString<const char*> formatString, Args &&... args) {
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>
void Error(FunctionString<std::string> formatString, Args &&... args) {
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>
void ErrorNoPrefix(S formatString, Args &&... args) {
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>
void Warn(FunctionString<const char*> formatString, Args &&... args) {
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>
void Warn(FunctionString<std::string> formatString, Args &&... args) {
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>
void WarnNoPrefix(S formatString, Args &&... args) {
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>
void Info(FunctionString<const char*> formatString, Args &&... args) {
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>
void Info(FunctionString<std::string> formatString, Args &&... args) {
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>
void InfoNoPrefix(S formatString, Args &&... args) {
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>
void Debug(FunctionString<const char*> formatString, Args &&... args) {
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>
void Debug(FunctionString<std::string> formatString, Args &&... args) {
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>
void DebugNoPrefix(S formatString, Args &&... args) {
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>
void Verbose(FunctionString<const char*> formatString, Args &&... args) {
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>
void Verbose(FunctionString<std::string> formatString, Args &&... args) {
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>
void VerboseNoPrefix(S formatString, Args &&... args) {
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 <dlfcn.h>
#include <unwind.h>
#include <fcntl.h>
#include "signal.h"
namespace skyline::signal {
@ -172,19 +173,21 @@ namespace skyline::signal {
void SetSignalHandler(std::initializer_list<int> signals, SignalHandler function, bool syscallRestart) {
static std::array<std::once_flag, NSIG> signalHandlerOnce{};
stack_t stack;
sigaltstack(nullptr, &stack);
struct sigaction action{
.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) {
std::call_once(signalHandlerOnce[signal], [signal, &action]() {
struct sigaction oldAction;
Sigaction(signal, &action, &oldAction);
if (oldAction.sa_flags && oldAction.sa_flags != action.sa_flags)
if (oldAction.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);
});

View File

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

View File

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

View File

@ -23,7 +23,7 @@ class RomProvider @Inject constructor(@ApplicationContext private val context :
if (file.isDirectory) {
addEntries(fileFormats, file, entries, systemLanguage)
} 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)
}
}

View File

@ -64,9 +64,10 @@ class SettingsActivity : AppCompatActivity() {
if (parentFragmentManager.findFragmentByTag(DIALOG_FRAGMENT_TAG) != null)
return
val f = IntegerListPreference.IntegerListPreferenceDialogFragmentCompat.newInstance(preference.getKey())
f.setTargetFragment(this, 0)
f.show(parentFragmentManager, DIALOG_FRAGMENT_TAG)
val dialogFragment = IntegerListPreference.IntegerListPreferenceDialogFragmentCompat.newInstance(preference.getKey())
@Suppress("DEPRECATION")
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 {
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]
*/
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
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]
*/
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
with(if (term.isEmpty()) {
@ -108,7 +108,7 @@ class GenericAdapter : RecyclerView.Adapter<GenericViewHolder<ViewBinding>>(), F
val filterData = mutableListOf<GenericListItem<*>>()
val topResults = extractSorted()
val avgScore = topResults.sumByDouble { it.score } / topResults.size
val avgScore = topResults.sumOf { it.score } / topResults.size
for (result in topResults)
if (result.score >= avgScore) filterData.add(result.item)

View File

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

View File

@ -8,11 +8,9 @@ package emu.skyline.adapter.controller
import android.view.ViewGroup
import androidx.core.view.isGone
import emu.skyline.adapter.GenericListItem
import emu.skyline.adapter.GenericViewHolder
import emu.skyline.adapter.ViewBindingFactory
import emu.skyline.adapter.inflater
import emu.skyline.databinding.ControllerItemBinding
import emu.skyline.input.InputManager
object ControllerBindingFactory : ViewBindingFactory {
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.content.Context
import android.os.Bundle
import android.os.VibrationEffect
import android.os.Vibrator
import android.os.*
import android.view.*
import android.view.animation.LinearInterpolator
import com.google.android.material.bottomsheet.BottomSheetBehavior
@ -64,7 +62,15 @@ class RumbleDialog @JvmOverloads constructor(val item : ControllerGeneralViewIte
if (context.id == 0) {
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.setOnClickListener {
controller.rumbleDeviceDescriptor = "builtin"
@ -85,7 +91,12 @@ class RumbleDialog @JvmOverloads constructor(val item : ControllerGeneralViewIte
// We want all input events from Joysticks and game pads
if (event.isFromSource(InputDevice.SOURCE_GAMEPAD) || event.isFromSource(InputDevice.SOURCE_JOYSTICK)) {
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 {
// 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()
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))
}
/**

View File

@ -5,24 +5,23 @@
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.Parcel
import android.os.Parcelable
import android.os.Parcelable.Creator
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.appcompat.app.AlertDialog
import androidx.core.content.res.TypedArrayUtils
import androidx.preference.R
import androidx.preference.DialogPreference
import androidx.preference.PreferenceDialogFragmentCompat
import emu.skyline.R as sR
import androidx.preference.R
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)
@ -177,19 +176,6 @@ class IntegerListPreference @JvmOverloads constructor(
super.writeToParcel(dest, flags)
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)
}
}
}
}
/**

View File

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

View File

@ -2,19 +2,18 @@
buildscript {
ext {
kotlin_version = '1.4.30'
kotlin_version = '1.5.31'
lifecycle_version = '2.3.1'
hilt_version = '2.33-beta'
hilt_version = '2.39.1'
lifecycle_version = '2.3.1'
}
repositories {
google()
mavenCentral()
jcenter()
}
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 "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
@ -28,7 +27,6 @@ allprojects {
google()
mavenCentral()
maven { url "https://google.bintray.com/flexbox-layout" }
jcenter()
}
}

View File

@ -21,3 +21,5 @@ android.useAndroidX=true
android.enableJetifier=true
# Android NDK verbose build output
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
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
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME