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
}

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)
throw exception("Old sigaction flags aren't equivalent to the replaced signal: {:#b} | {:#b}", 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,15 +433,25 @@ 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
device.vibrator
vibrators[index] = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
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.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)
@ -131,4 +131,4 @@ class GenericAdapter : RecyclerView.Adapter<GenericViewHolder<ViewBinding>>(), F
onFilterPublishedListener?.invoke()
}
}
}
}

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))
}
/**
@ -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
*/
private external fun populate(format : Int, romFd : Int, appFilesPath : String, systemLanguage : Int) : Int
}
}

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)
}
}
}
}
/**
@ -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 {
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