init devmenu models

This commit is contained in:
2023-10-20 18:04:34 +03:00
parent b3b6bbbb6b
commit d955b65ea2
53 changed files with 809 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
/build
+69
View File
@@ -0,0 +1,69 @@
@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
plugins {
alias(libs.plugins.androidLibrary)
alias(libs.plugins.kotlinAndroid)
}
android {
namespace = "com.megboyzz.devmenu"
compileSdk = 34
defaultConfig {
minSdk = 21
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
vectorDrawables {
useSupportLibrary = true
}
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.1"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
}
dependencies {
implementation(libs.core.ktx)
implementation(libs.appcompat)
implementation(libs.material)
implementation(libs.lifecycle.runtime.ktx)
implementation(libs.activity.compose)
implementation(platform(libs.compose.bom))
implementation(libs.ui)
implementation(libs.ui.graphics)
implementation(libs.ui.tooling.preview)
implementation(libs.material3)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.test.ext.junit)
androidTestImplementation(libs.espresso.core)
androidTestImplementation(platform(libs.compose.bom))
androidTestImplementation(libs.ui.test.junit4)
debugImplementation(libs.ui.tooling)
debugImplementation(libs.ui.test.manifest)
}
View File
+1
View File
@@ -0,0 +1 @@
/build
+44
View File
@@ -0,0 +1,44 @@
@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
plugins {
alias(libs.plugins.androidLibrary)
alias(libs.plugins.kotlinAndroid)
}
android {
namespace = "com.megboyzz.devmenu.data"
compileSdk = 34
defaultConfig {
minSdk = 21
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
implementation(libs.core.ktx)
implementation(libs.appcompat)
implementation(libs.material)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.test.ext.junit)
androidTestImplementation(libs.espresso.core)
}
View File
+21
View File
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
@@ -0,0 +1,24 @@
package com.megboyzz.devmenu.data
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.megboyzz.devmenu.data.test", appContext.packageName)
}
}
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>
@@ -0,0 +1,22 @@
package com.megboyzz.devmenu.data.util
import android.os.FileObserver
import android.util.Log
import java.io.File
class SaveFileObserver(
private val source: File,
private val destinationFolder: File
) : FileObserver(source.absolutePath) {
override fun onEvent(event: Int, path: String?) {
if (event and CREATE != 0 || event and MODIFY != 0) {
Log.d("FileObserver", "File created or modified: $source")
source copyToThe destinationFolder
}
}
private infix fun File.copyToThe(destination: File) = this.copyTo(destination)
}
@@ -0,0 +1,17 @@
package com.megboyzz.devmenu.data
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}
+1
View File
@@ -0,0 +1 @@
/build
+22
View File
@@ -0,0 +1,22 @@
plugins {
id("java-library")
alias(libs.plugins.org.jetbrains.kotlin.jvm)
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
dependencies {
implementation("javax.inject:javax.inject:1")
testImplementation("junit:junit:4.13.2")
testImplementation("org.mockito:mockito-core:3.12.4")
testImplementation("org.mockito.kotlin:mockito-kotlin:3.12.4")
}
sourceSets {
test {
java.srcDir("src/test/java") // Путь к исходным файлам тестов на Kotlin
}
}
@@ -0,0 +1,3 @@
package com.megboyzz.devmenu.domain.entities
val devmenuId = "DevMenu"
@@ -0,0 +1,9 @@
package com.megboyzz.devmenu.domain.entities
import java.util.Date
data class FileProps(
val fileName: String,
val size: Int,
val dateOfCreation: Date
)
@@ -0,0 +1,19 @@
package com.megboyzz.devmenu.domain.entities
enum class GameLang(private val lang: String) {
System("sys"),
Chinese("cn"),
Dutch("nl"),
English("en"),
French("fr"),
Deutsch("de"),
Italian("it"),
Japanese("ja"),
Korean("kr"),
Portuguese("br"),
Russian("ru"),
Spanish("es");
override fun toString() = lang
}
@@ -0,0 +1,8 @@
package com.megboyzz.devmenu.domain.entities
data class Settings(
val enable: Boolean,
val trackingInterval: Int,
val pathToSaveTrackable: String,
val lang: GameLang
)
@@ -0,0 +1,8 @@
package com.megboyzz.devmenu.domain.entities
import java.util.Date
data class SvmwProps(
val description: String,
val date: Date
)
@@ -0,0 +1,14 @@
package com.megboyzz.devmenu.domain.repository
import com.megboyzz.devmenu.domain.entities.FileProps
import java.io.File
interface FilesRepository {
val internalRoot: File
val externalRoot: File
fun getFileProps(file: File): FileProps
}
@@ -0,0 +1,13 @@
package com.megboyzz.devmenu.domain.repository
import java.io.File
interface ReplacementRepository {
suspend fun replaceFile(what: File, to: File): Result<Boolean>
suspend fun recoverReplacement(replacement: File): Result<Boolean>
suspend fun getAllReplacements(): List<File>
}
@@ -0,0 +1,11 @@
package com.megboyzz.devmenu.domain.repository
import java.io.File
interface SaveRepository {
suspend fun loadSaveFile(file: File): Boolean
suspend fun uploadCurrentSaveFile(fileToUpload: File): Boolean
}
@@ -0,0 +1,11 @@
package com.megboyzz.devmenu.domain.repository
import com.megboyzz.devmenu.domain.entities.Settings
interface SettingsRepository {
suspend fun getSettings(): Settings
suspend fun setSettings(settings: Settings)
}
@@ -0,0 +1,20 @@
package com.megboyzz.devmenu.domain.repository
import com.megboyzz.devmenu.domain.entities.SvmwProps
import java.io.File
interface SvmwRepository {
suspend fun createSvmwFromCurrentSaveFileAndSetIt(): Boolean
suspend fun createSvmwFromCurrentSaveFile(fileToSave: File): Boolean
suspend fun createSvmw(fileToSave: File, saveFile: File): Boolean
suspend fun loadSvmw(svmw: File): Boolean
suspend fun getInfoAboutSvmw(svmw: File): SvmwProps?
suspend fun getInfoAboutLoadedSvmw(): SvmwProps?
}
@@ -0,0 +1,11 @@
package com.megboyzz.devmenu.domain.usecases.files
import com.megboyzz.devmenu.domain.repository.FilesRepository
import java.io.File
import javax.inject.Inject
class GetExternalFilesUseCase @Inject constructor(
private val filesRepository: FilesRepository
) {
operator fun invoke(childPath: String = ""): File = File(filesRepository.externalRoot, childPath)
}
@@ -0,0 +1,12 @@
package com.megboyzz.devmenu.domain.usecases.files
import com.megboyzz.devmenu.domain.entities.FileProps
import com.megboyzz.devmenu.domain.repository.FilesRepository
import java.io.File
import javax.inject.Inject
class GetFilePropsUseCase @Inject constructor(
private val filesRepository: FilesRepository
) {
operator fun invoke(file: File): FileProps = filesRepository.getFileProps(file)
}
@@ -0,0 +1,13 @@
package com.megboyzz.devmenu.domain.usecases.files
import com.megboyzz.devmenu.domain.repository.FilesRepository
import java.io.File
import javax.inject.Inject
class GetInternalFilesUseCase @Inject constructor(
private val filesRepository: FilesRepository
) {
operator fun invoke(childPath: String = ""): File = File(filesRepository.internalRoot, childPath)
}
@@ -0,0 +1,7 @@
package com.megboyzz.devmenu.domain.usecases.files
import java.io.File
class RemoveFileUseCase {
operator fun invoke(file: File): Boolean = file.delete()
}
@@ -0,0 +1,14 @@
package com.megboyzz.devmenu.domain.usecases.files
import com.megboyzz.devmenu.domain.repository.FilesRepository
import javax.inject.Inject
class RemoveInternalDataUseCase @Inject constructor(
private val filesRepository: FilesRepository
) {
operator fun invoke(): Boolean{
val listFiles = filesRepository.internalRoot.listFiles() ?: return false
listFiles.forEach { it.deleteRecursively() }
return true
}
}
@@ -0,0 +1,14 @@
package com.megboyzz.devmenu.domain.usecases.files
import com.megboyzz.devmenu.domain.repository.ReplacementRepository
import java.io.File
import javax.inject.Inject
class ReplaceFileUseCase @Inject constructor(
private val replacementRepository: ReplacementRepository
) {
suspend operator fun invoke(what: File, to: File): Result<Boolean>
= replacementRepository.replaceFile(what, to)
}
@@ -0,0 +1,10 @@
package com.megboyzz.devmenu.domain.usecases.replacement
import com.megboyzz.devmenu.domain.repository.ReplacementRepository
import javax.inject.Inject
class GetAllReplacedFilesUseCase @Inject constructor(
private val replacementRepository: ReplacementRepository
) {
suspend operator fun invoke() = replacementRepository.getAllReplacements()
}
@@ -0,0 +1,13 @@
package com.megboyzz.devmenu.domain.usecases.replacement
import javax.inject.Inject
class RecoverAllReplacementsUseCase @Inject constructor(
private val getAllReplacedFilesUseCase: GetAllReplacedFilesUseCase,
private val recoverReplacementUseCase: RecoverReplacementUseCase
) {
suspend operator fun invoke(){
val listReplacedFiles = getAllReplacedFilesUseCase()
listReplacedFiles.forEach { recoverReplacementUseCase(it) }
}
}
@@ -0,0 +1,11 @@
package com.megboyzz.devmenu.domain.usecases.replacement
import com.megboyzz.devmenu.domain.repository.ReplacementRepository
import java.io.File
import javax.inject.Inject
class RecoverReplacementUseCase @Inject constructor(
private val replacementRepository: ReplacementRepository
) {
suspend operator fun invoke(replacement: File) = replacementRepository.recoverReplacement(replacement)
}
@@ -0,0 +1,11 @@
package com.megboyzz.devmenu.domain.usecases.save
import com.megboyzz.devmenu.domain.repository.SaveRepository
import java.io.File
import javax.inject.Inject
class LoadSaveFileUseCase @Inject constructor(
private val saveRepository: SaveRepository
) {
suspend operator fun invoke(file: File) = if(file.exists()) saveRepository.loadSaveFile(file) else false
}
@@ -0,0 +1,15 @@
package com.megboyzz.devmenu.domain.usecases.save
import com.megboyzz.devmenu.domain.repository.SaveRepository
import java.io.File
import javax.inject.Inject
class UploadSaveFileUseCase @Inject constructor(
private val saveRepository: SaveRepository
) {
suspend operator fun invoke(fileToUpload: File) =
if(fileToUpload.exists())
saveRepository.uploadCurrentSaveFile(fileToUpload)
else
false
}
@@ -0,0 +1,11 @@
package com.megboyzz.devmenu.domain.usecases.settings
import com.megboyzz.devmenu.domain.entities.Settings
import com.megboyzz.devmenu.domain.repository.SettingsRepository
import javax.inject.Inject
class GetSettingsUseCase @Inject constructor(
private val settingsRepository: SettingsRepository
) {
suspend operator fun invoke(): Settings = settingsRepository.getSettings()
}
@@ -0,0 +1,11 @@
package com.megboyzz.devmenu.domain.usecases.settings
import com.megboyzz.devmenu.domain.entities.Settings
import com.megboyzz.devmenu.domain.repository.SettingsRepository
import javax.inject.Inject
class SetSettingsUseCase @Inject constructor(
private val settingsRepository: SettingsRepository
) {
suspend operator fun invoke(settings: Settings) = settingsRepository.setSettings(settings)
}
@@ -0,0 +1,12 @@
package com.megboyzz.devmenu.domain.usecases.settings
import com.megboyzz.devmenu.domain.entities.devmenuId
import com.megboyzz.devmenu.domain.repository.FilesRepository
import java.io.File
import javax.inject.Inject
class TurnOffDevMenuUseCase @Inject constructor(
private val filesRepository: FilesRepository
) {
operator fun invoke() = File(filesRepository.externalRoot, devmenuId).delete()
}
@@ -0,0 +1,27 @@
package com.megboyzz.devmenu.domain.usecases.svmw
import com.megboyzz.devmenu.domain.repository.SvmwRepository
import java.io.File
import javax.inject.Inject
class CreateSvmwFileUseCase @Inject constructor(
private val svmwRepository: SvmwRepository
) {
suspend operator fun invoke(
fileToSave: File? = null,
saveFile: File? = null
){
val a = fileToSave != null
val b = saveFile != null
when {
a && b -> svmwRepository.createSvmw(fileToSave!!, saveFile!!)
a && !b -> svmwRepository.createSvmwFromCurrentSaveFile(fileToSave!!)
!a && !b -> svmwRepository.createSvmwFromCurrentSaveFileAndSetIt()
}
}
}
@@ -0,0 +1,11 @@
package com.megboyzz.devmenu.domain.usecases.svmw
import com.megboyzz.devmenu.domain.repository.SvmwRepository
import java.io.File
import javax.inject.Inject
class GetSvmwFilePropsUseCase @Inject constructor(
private val svmwRepository: SvmwRepository
) {
suspend operator fun invoke(file: File) = svmwRepository.getInfoAboutSvmw(file)
}
@@ -0,0 +1,10 @@
package com.megboyzz.devmenu.domain.usecases.svmw
import com.megboyzz.devmenu.domain.repository.SvmwRepository
import javax.inject.Inject
class GetSvmwPropsUseCase @Inject constructor(
private val svmwRepository: SvmwRepository
) {
suspend operator fun invoke() = svmwRepository.getInfoAboutLoadedSvmw()
}
@@ -0,0 +1,11 @@
package com.megboyzz.devmenu.domain.usecases.svmw
import com.megboyzz.devmenu.domain.repository.SvmwRepository
import java.io.File
import javax.inject.Inject
class LoadSvmwUseCase @Inject constructor(
private val svmwRepository: SvmwRepository
) {
suspend operator fun invoke(file: File) = if(file.exists()) svmwRepository.loadSvmw(file) else false
}
@@ -0,0 +1,33 @@
import com.megboyzz.devmenu.domain.repository.FilesRepository
import com.megboyzz.devmenu.domain.repository.ReplacementRepository
import com.megboyzz.devmenu.domain.repository.SaveRepository
import com.megboyzz.devmenu.domain.repository.SettingsRepository
import com.megboyzz.devmenu.domain.repository.SvmwRepository
import org.mockito.Mockito.mock
import org.mockito.Mockito.`when`
import java.io.File
import kotlin.coroutines.coroutineContext
class UseCaseTest {
private val filesRepository = mock(FilesRepository::class.java)
private val replacementRepository = mock(ReplacementRepository::class.java)
private val saveRepository = mock(SaveRepository::class.java)
private val settingsRepository = mock(SettingsRepository::class.java)
private val svmwRepository = mock(SvmwRepository::class.java)
private val externalFile = File("D:\\NFSMW Online\\com.ea.games.nfs13_na")
private val internalFile = File("D:\\NFSMW Online")
init {
`when`(filesRepository.externalRoot).thenReturn(externalFile)
`when`(filesRepository.internalRoot).thenReturn(internalFile)
//`when`(replacementRepository.getAllReplacements()).thenReturn()
}
}
+21
View File
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
@@ -0,0 +1,24 @@
package com.megboyzz.devmenu
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.megboyzz.devmenu.test", appContext.packageName)
}
}
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/title_activity_main"
android:theme="@style/Theme.DevMenu" />
</application>
</manifest>
@@ -0,0 +1,12 @@
package com.megboyzz.devmenu
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {}
}
}
@@ -0,0 +1,11 @@
package com.megboyzz.devmenu.ui.theme
import androidx.compose.ui.graphics.Color
val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)
@@ -0,0 +1,68 @@
package com.megboyzz.devmenu.ui.theme
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
private val DarkColorScheme = darkColorScheme(
primary = Purple80,
secondary = PurpleGrey80,
tertiary = Pink80
)
private val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40,
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
onPrimary = Color.White,
onSecondary = Color.White,
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
)
@Composable
fun DevMenuTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
window.statusBarColor = colorScheme.primary.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
}
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}
@@ -0,0 +1,34 @@
package com.megboyzz.devmenu.ui.theme
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with
val Typography = Typography(
bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)
/* Other default text styles to override
titleLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 22.sp,
lineHeight = 28.sp,
letterSpacing = 0.sp
),
labelSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 11.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp
)
*/
)
+3
View File
@@ -0,0 +1,3 @@
<resources>
<string name="title_activity_main">MainActivity</string>
</resources>
+5
View File
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.DevMenu" parent="android:Theme.Material.Light.NoActionBar" />
</resources>
@@ -0,0 +1,17 @@
package com.megboyzz.devmenu
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}
+3
View File
@@ -21,3 +21,6 @@ dependencyResolutionManagement {
rootProject.name = "NFS Most Wanted" rootProject.name = "NFS Most Wanted"
include(":app") include(":app")
include(":devmenu")
include(":devmenu:domain")
include(":devmenu:data")