add mpcore module, settings screen done

This commit is contained in:
2024-01-03 00:35:39 +04:00
parent d078c200fa
commit b093674b6e
32 changed files with 602 additions and 66 deletions
@@ -63,7 +63,7 @@ public class UtilitiesAndData {
public static void printLog(String msg){ public static void printLog(String msg){
try{ try{
if(isLoggerEnabled()) if(isLoggerEnabled())
stream.write(msg.getBytes(StandardCharsets.UTF_8)); stream.write(msg.getBytes(StandardCharsets.UTF_8));
} catch (IOException e) { } catch (IOException e) {
Log.wtf(LOG_TAG, "cant write to stream((("); Log.wtf(LOG_TAG, "cant write to stream(((");
e.printStackTrace(); e.printStackTrace();
+5
View File
@@ -32,6 +32,7 @@ android {
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8
isCoreLibraryDesugaringEnabled = true
} }
kotlinOptions { kotlinOptions {
jvmTarget = "1.8" jvmTarget = "1.8"
@@ -71,6 +72,10 @@ dependencies {
debugImplementation(libs.ui.tooling) debugImplementation(libs.ui.tooling)
debugImplementation(libs.ui.test.manifest) debugImplementation(libs.ui.test.manifest)
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4")
implementation(libs.compose.settings.ui.m3)
implementation(libs.voyager.navigator) implementation(libs.voyager.navigator)
implementation(libs.voyager.bottomSheetNavigator) implementation(libs.voyager.bottomSheetNavigator)
implementation(libs.voyager.transitions) implementation(libs.voyager.transitions)
@@ -4,6 +4,8 @@ import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.bottomSheet.BottomSheetNavigator import cafe.adriel.voyager.navigator.bottomSheet.BottomSheetNavigator
import com.megboyzz.devmenu.ui.components.MainScaffold import com.megboyzz.devmenu.ui.components.MainScaffold
@@ -34,7 +36,9 @@ class MainActivity : ComponentActivity() {
} }
setContent { setContent {
BottomSheetNavigator { BottomSheetNavigator(
sheetShape = RoundedCornerShape(topStart = 10.dp, topEnd = 10.dp)
) {
Navigator(HomeScreen(::runGame)) Navigator(HomeScreen(::runGame))
} }
} }
@@ -1,5 +1,6 @@
package com.megboyzz.devmenu.ui package com.megboyzz.devmenu.ui
import android.annotation.SuppressLint
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
@@ -8,6 +9,11 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import java.text.CharacterIterator
import java.text.SimpleDateFormat
import java.text.StringCharacterIterator
import java.util.Date
@Composable @Composable
fun Int.asPainter() = painterResource(this) fun Int.asPainter() = painterResource(this)
@@ -23,6 +29,22 @@ fun SpacerWidth(width: Dp) = Spacer(Modifier.width(width))
@Composable @Composable
fun SpacerHeight(height: Dp) = Spacer(Modifier.height(height)) fun SpacerHeight(height: Dp) = Spacer(Modifier.height(height))
fun humanReadableByteCountSI(bytes: Long): String {
var b = bytes
if (-1000 < b && b < 1000) {
return "$b B"
}
val ci: CharacterIterator = StringCharacterIterator("kMGTPE")
while (b <= -999950 || b >= 999950) {
b /= 1000
ci.next()
}
return String.format("%.1f %cB", b / 1000.0, ci.current())
}
@SuppressLint("SimpleDateFormat")
fun formatDate(date: Date) = SimpleDateFormat("dd.MM.yyyy HH:mm:ss").format(date)
/** /**
* Возвращает лямбду того типа которая требуется в Composable контексте<br> * Возвращает лямбду того типа которая требуется в Composable контексте<br>
@@ -4,6 +4,8 @@ import androidx.compose.animation.core.tween
import androidx.compose.animation.expandIn import androidx.compose.animation.expandIn
import androidx.compose.animation.shrinkOut import androidx.compose.animation.shrinkOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@@ -16,3 +18,11 @@ val exit = shrinkOut(
shrinkTowards = Alignment.CenterStart, shrinkTowards = Alignment.CenterStart,
animationSpec = tween(250) animationSpec = tween(250)
) )
fun searchFieldEnter(duration: Int) = slideInVertically(animationSpec = tween(durationMillis = duration)) {
-it / 3
}
fun searchFieldExit(duration: Int) = slideOutVertically(animationSpec = tween(durationMillis = duration)) {
-it / 3
}
@@ -29,8 +29,11 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.megboyzz.devmenu.R import com.megboyzz.devmenu.R
import com.megboyzz.devmenu.ui.SpacerHeight
import com.megboyzz.devmenu.ui.asPainter import com.megboyzz.devmenu.ui.asPainter
import com.megboyzz.devmenu.ui.entities.FsElementModel import com.megboyzz.devmenu.ui.models.FsElementModel
import com.megboyzz.devmenu.ui.formatDate
import com.megboyzz.devmenu.ui.humanReadableByteCountSI
@Composable @Composable
fun ClickableMenuPosition( fun ClickableMenuPosition(
@@ -112,9 +115,7 @@ fun FileContextMenu(
var state by remember { mutableStateOf(ContextMenuState.Main) } var state by remember { mutableStateOf(ContextMenuState.Main) }
CoreFileContextMenu { CoreFileContextMenu(fsElementEntity = fsElementEntity) {
FsElementInContextMenu(name = fsElementEntity.name, type = fsElementEntity.type)
Divider()
AnimatedBoxForContextMenuContent(isVisible = state == ContextMenuState.Main){ AnimatedBoxForContextMenuContent(isVisible = state == ContextMenuState.Main){
AnimatedBoxForContextMenuContent(isVisible = innerType == ElementType.isFile) { AnimatedBoxForContextMenuContent(isVisible = innerType == ElementType.isFile) {
ClickableMenuPosition( ClickableMenuPosition(
@@ -154,7 +155,7 @@ fun FileContextMenu(
ClickableMenuPosition( ClickableMenuPosition(
name = "Свойства", name = "Свойства",
icon = R.drawable.info.asPainter(), icon = R.drawable.info.asPainter(),
onClick = {} onClick = { state = ContextMenuState.Props }
) )
ClickableMenuPosition( ClickableMenuPosition(
name = "Удалить", name = "Удалить",
@@ -181,6 +182,7 @@ fun FileContextMenu(
icon = R.drawable.info.asPainter(), icon = R.drawable.info.asPainter(),
withEnd = false, withEnd = false,
) )
PropsView(fsElementEntity = fsElementEntity)
RemoveButton(text = "Назад") { RemoveButton(text = "Назад") {
state = ContextMenuState.Main state = ContextMenuState.Main
} }
@@ -188,6 +190,17 @@ fun FileContextMenu(
} }
} }
@Composable
fun PropsView(
fsElementEntity: FsElementModel
) {
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
Text(text = "Изменен: ${formatDate(fsElementEntity.date)}")
Text(text = "Размер: ${humanReadableByteCountSI(fsElementEntity.size)} (${fsElementEntity.size} B)")
}
}
@Composable @Composable
@@ -215,9 +228,8 @@ fun RemoveFileButtonsGroup(
Row( Row(
horizontalArrangement = Arrangement.spacedBy(12.dp), horizontalArrangement = Arrangement.spacedBy(12.dp),
) { ) {
val m = Modifier.weight(1f) RemoveButton(text = "Удалить", onClick = onDelete, modifier = Modifier.weight(1f))
RemoveButton(text = "Удалить", onClick = onDelete, modifier = m) RemoveButton(text = "Отмена", onClick = onCancel, modifier = Modifier.weight(1f))
RemoveButton(text = "Отмена", onClick = onCancel, modifier = m)
} }
} }
@@ -239,6 +251,7 @@ fun RemoveButton(
@Composable @Composable
fun CoreFileContextMenu( fun CoreFileContextMenu(
fsElementEntity: FsElementModel,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
@@ -264,7 +277,12 @@ fun CoreFileContextMenu(
vertical = 32.dp vertical = 32.dp
), ),
){ ){
Column(verticalArrangement = Arrangement.spacedBy(20.dp)) { Column {
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
FsElementInContextMenu(name = fsElementEntity.name, type = fsElementEntity.type)
Divider()
SpacerHeight(height = 0.dp)
}
content() content()
} }
} }
@@ -7,7 +7,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.megboyzz.devmenu.ui.entities.FsElementModel import com.megboyzz.devmenu.ui.models.FsElementModel
@Composable @Composable
fun FsElement( fun FsElement(
@@ -0,0 +1,55 @@
package com.megboyzz.devmenu.ui.components
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.megboyzz.devmenu.R
import com.megboyzz.devmenu.ui.asPainter
@Composable
fun CoreBar(folders: List<String>) {
LazyRow(verticalAlignment = Alignment.CenterVertically) {
items(folders){
Text(
text = it,
style = MaterialTheme.typography.labelSmall
)
Image(
painter = R.drawable.enter.asPainter(),
modifier = Modifier
.padding(4.dp)
.size(8.dp),
contentDescription = null
)
}
}
}
@Composable
fun StatusBarInCurFolder(
path: String
) {
if(path.isEmpty()) return
val pathAsArray = path.split("/").filter { it.isNotEmpty() }
CoreBar(folders = pathAsArray)
}
@Preview
@Composable
fun CoreBarPrev() {
val path = "/data/data/com.ea.games.nfs13_na/files/var/".split("/").filter { it.isNotEmpty() }
CoreBar(folders = path)
}
@@ -1,10 +1,5 @@
package com.megboyzz.devmenu.ui.components package com.megboyzz.devmenu.ui.components
import android.widget.Toast
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@@ -38,7 +33,6 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.megboyzz.devmenu.ui.asComposableLambda
import com.megboyzz.devmenu.ui.theme.DevMenuTheme import com.megboyzz.devmenu.ui.theme.DevMenuTheme
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@@ -92,28 +86,15 @@ fun TopBar(
val context = LocalContext.current val context = LocalContext.current
TopAppBar( TopAppBar(
title = { title = {
Box { if(isSearching){
AnimatedVisibility( SearchFileTextField(
visible = isSearching, value = searchValue,
enter = slideInVertically(animationSpec = tween(durationMillis = 200)) { onValueChange = onSearchValueChanged
-it / 3 )
}, }else Text("NFSMW DevMenu")
exit = slideOutVertically(animationSpec = tween(durationMillis = 200)) {
-it / 3
}
) {
SearchFileTextField(
value = searchValue,
onValueChange = onSearchValueChanged
)
}
if(!isSearching) Text("NFSMW DevMenu")
}
}, },
actions = { actions = {
SearchButton { SearchButton {
Toast.makeText(context, "hh", Toast.LENGTH_LONG).show()
isSearching = !isSearching isSearching = !isSearching
} }
SettingsButton(onClick = onSettingsClick) SettingsButton(onClick = onSettingsClick)
@@ -169,9 +150,7 @@ fun SearchFileTextField(
value = value, value = value,
onValueChange = onValueChange, onValueChange = onValueChange,
decorationBox = { innerTextField -> decorationBox = { innerTextField ->
Box( Box(Modifier.padding(end = 12.dp)) {
) {
Row( Row(
Modifier Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -0,0 +1,211 @@
package com.megboyzz.devmenu.ui.components
import android.media.audiofx.Equalizer.Settings
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.sharp.Home
import androidx.compose.material.icons.sharp.List
import androidx.compose.material.icons.sharp.Notifications
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Divider
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.alorma.compose.settings.storage.base.getValue
import com.alorma.compose.settings.storage.base.rememberBooleanSettingState
import com.alorma.compose.settings.storage.base.rememberIntSettingState
import com.alorma.compose.settings.ui.SettingsCheckbox
import com.alorma.compose.settings.ui.SettingsList
import com.alorma.compose.settings.ui.SettingsMenuLink
import com.alorma.compose.settings.ui.SettingsSwitch
import com.megboyzz.devmenu.R
import com.megboyzz.devmenu.ui.SpacerHeight
import com.megboyzz.devmenu.ui.asPainter
import com.megboyzz.devmenu.ui.models.GameLangModel
@Composable
fun SettingsScaffold(
onBackClick: () -> Unit,
content: @Composable ColumnScope.() -> Unit
) {
Scaffold(
topBar = { SettingsTopBar(onBackClick = onBackClick) },
){
Column {
SpacerHeight(height = it.calculateTopPadding())
content(this)
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SettingsTopBar(
onBackClick: () -> Unit
) {
TopAppBar(
navigationIcon = {
IconButton(onClick = onBackClick) {
Icon(imageVector = Icons.Filled.ArrowBack, contentDescription = null)
}
},
title = { Text("Настройки") }
)
}
@Composable
fun LangDialog(
langModel: GameLangModel,
onUpdateLanguage: (GameLangModel) -> Unit
) {
val items = GameLangModel.values().map { it.name }
val curItem = items.indexOf(langModel.name)
val lang = rememberIntSettingState(curItem)
SettingsList(
icon = { Icon(painter = R.drawable.lang.asPainter(), contentDescription = null) },
title = { Text(text = "Сменить язык") },
items = items,
state = lang,
onItemSelected = { index, _ ->
onUpdateLanguage(GameLangModel.values()[index])
}
)
}
@Composable
fun DevMenuOffDialog(
onDevMenuOff: () -> Unit,
onDismiss: () -> Unit,
) {
AlertDialog(
title = {
Text(text = "Выключить DevMenu")
},
text = {
Text(text = "Уверен что хочешь выключить DevMenu? Чтобы его включить нужно будет создать пустой файл c именем DevMenu в корне кэша игры")
},
onDismissRequest = onDismiss,
confirmButton = {
TextButton(
onClick = onDevMenuOff
) {
Text("OK")
}
},
dismissButton = {
TextButton(
onClick = onDismiss
) {
Text("Отмена")
}
}
)
}
@Composable
fun SettingsScreenContent(
onBackClick: () -> Unit,
langModel: GameLangModel,
onUpdateLanguage: (GameLangModel) -> Unit,
isTrackingSaveFile: Boolean,
onUpdateTrackingSaveFileState: (Boolean) -> Unit,
onChooseTrackingPathToSave: () -> Unit,
onUploadSaveFile: () -> Unit,
onDownloadSaveFile: () -> Unit,
onDevMenuOff: () -> Unit
) {
var isDevMenuOffDialog by remember { mutableStateOf(false) }
if(isDevMenuOffDialog){
DevMenuOffDialog(
onDevMenuOff = {
onDevMenuOff()
isDevMenuOffDialog =! isDevMenuOffDialog
},
onDismiss = { isDevMenuOffDialog =! isDevMenuOffDialog }
)
}
SettingsScaffold(onBackClick = onBackClick) {
LangDialog(langModel = langModel, onUpdateLanguage = onUpdateLanguage)
Divider()
SettingsSwitch(
icon = {
Icon(painter = R.drawable.tracking.asPainter(), contentDescription = null)
},
title = {
Text(text = "Отслеживание файла сохранения")
},
state = rememberBooleanSettingState(isTrackingSaveFile),
onCheckedChange = onUpdateTrackingSaveFileState
)
SettingsMenuLink(
icon = { Icon(painter = R.drawable.folder.asPainter(), contentDescription = null) },
title = { Text(text = "Выбрать путь сохранения отслеживаемого сохранения") },
onClick = onChooseTrackingPathToSave,
)
Divider()
SettingsMenuLink(
icon = { Icon(painter = R.drawable.download.asPainter(), contentDescription = null) },
title = { Text(text = "Выгрузить файл сохранения") },
subtitle = { Text(text = "Получить текущий nfstr_save.sb") },
onClick = onDownloadSaveFile,
)
SettingsMenuLink(
icon = { Icon(painter = R.drawable.upload.asPainter(), contentDescription = null) },
title = { Text(text = "Загрузить файл сохранения") },
subtitle = { Text(text = "Загрузить в игру nfstr_save.sb") },
onClick = onUploadSaveFile,
)
Divider()
SettingsMenuLink(
title = {
Text(
text = "Выключить DevMenu",
color = Color.Red
)
},
onClick = { isDevMenuOffDialog =! isDevMenuOffDialog },
)
}
}
@Preview
@Composable
fun SettingsTopBarPreview() {
SettingsScreenContent(
onBackClick = { /*TODO*/ },
langModel = GameLangModel.System,
onUpdateLanguage = {},
isTrackingSaveFile = false,
onUpdateTrackingSaveFileState = {},
onChooseTrackingPathToSave = { /*TODO*/ },
onUploadSaveFile = { /*TODO*/ },
onDownloadSaveFile = { /*TODO*/ },
onDevMenuOff = {})
}
@@ -1,4 +1,4 @@
package com.megboyzz.devmenu.ui.entities package com.megboyzz.devmenu.ui.models
import com.megboyzz.devmenu.ui.components.ElementType import com.megboyzz.devmenu.ui.components.ElementType
import java.util.Date import java.util.Date
@@ -0,0 +1,19 @@
package com.megboyzz.devmenu.ui.models
enum class GameLangModel(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
}
@@ -3,22 +3,16 @@ package com.megboyzz.devmenu.ui.screens
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.core.screen.Screen
import com.megboyzz.devmenu.ui.components.FileContextMenu import com.megboyzz.devmenu.ui.components.FileContextMenu
import com.megboyzz.devmenu.ui.entities.FsElementModel import com.megboyzz.devmenu.ui.models.FsElementModel
class FileContextMenuScreen( class FileContextMenuScreen(
private val fsElementModel: FsElementModel, private val fsElementModel: FsElementModel,
private val onRemoveFile: () -> Unit, private val onRemoveFile: () -> Unit,
) : Screen { ) : Screen {
@Composable @Composable
override fun Content() { override fun Content() = FileContextMenu(
fsElementEntity = fsElementModel,
onRemoveElement = onRemoveFile,
onUpdateType = {null}
FileContextMenu( )
fsElementEntity = fsElementModel,
onRemoveElement = onRemoveFile,
onUpdateType = {null}
)
}
} }
@@ -1,5 +1,6 @@
package com.megboyzz.devmenu.ui.screens package com.megboyzz.devmenu.ui.screens
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@@ -7,10 +8,11 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator import cafe.adriel.voyager.navigator.bottomSheet.LocalBottomSheetNavigator
import com.megboyzz.devmenu.ui.SpacerHeight
import com.megboyzz.devmenu.ui.components.ElementType import com.megboyzz.devmenu.ui.components.ElementType
import com.megboyzz.devmenu.ui.components.FsElement import com.megboyzz.devmenu.ui.components.FsElement
import com.megboyzz.devmenu.ui.components.MainScaffold import com.megboyzz.devmenu.ui.components.MainScaffold
import com.megboyzz.devmenu.ui.entities.FsElementModel import com.megboyzz.devmenu.ui.models.FsElementModel
import java.util.Date import java.util.Date
class HomeScreen( class HomeScreen(
@@ -38,18 +40,22 @@ class HomeScreen(
searchValue = searchValue, searchValue = searchValue,
onFloatingButtonClick = onFloatingButtonClick onFloatingButtonClick = onFloatingButtonClick
) { ) {
FsElement( Column {
fsElementEntity = fsElementModel, SpacerHeight(height = it.calculateTopPadding())
onClick = { /*TODO*/ }, FsElement(
onLongPressClick = { fsElementEntity = fsElementModel,
bottomSheetNavigator.show( onClick = { /*TODO*/ },
FileContextMenuScreen( onLongPressClick = {
fsElementModel = fsElementModel, bottomSheetNavigator.show(
onRemoveFile = {} FileContextMenuScreen(
fsElementModel = fsElementModel,
onRemoveFile = {}
)
) )
) }
} )
) }
} }
} }
@@ -0,0 +1,11 @@
package com.megboyzz.devmenu.ui.screens
import androidx.compose.runtime.Composable
import cafe.adriel.voyager.core.screen.Screen
class SettingsScreen : Screen {
@Composable
override fun Content() {
TODO("Not yet implemented")
}
}
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#FF000000"
android:pathData="M480,640 L280,440l56,-58 104,104v-326h80v326l104,-104 56,58 -200,200ZM240,800q-33,0 -56.5,-23.5T160,720v-120h80v120h480v-120h80v120q0,33 -23.5,56.5T720,800L240,800Z"/>
</vector>
+9
View File
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#FF000000"
android:pathData="M160,800q-33,0 -56.5,-23.5T80,720v-480q0,-33 23.5,-56.5T160,160h240l80,80h320q33,0 56.5,23.5T880,320v400q0,33 -23.5,56.5T800,800L160,800ZM160,720h640v-400L447,320l-80,-80L160,240v480ZM160,720v-480,480Z"/>
</vector>
+9
View File
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#FF000000"
android:pathData="M480,880q-82,0 -155,-31.5t-127.5,-86Q143,708 111.5,635T80,480q0,-83 31.5,-155.5t86,-127Q252,143 325,111.5T480,80q83,0 155.5,31.5t127,86q54.5,54.5 86,127T880,480q0,82 -31.5,155t-86,127.5q-54.5,54.5 -127,86T480,880ZM480,798q26,-36 45,-75t31,-83L404,640q12,44 31,83t45,75ZM376,782q-18,-33 -31.5,-68.5T322,640L204,640q29,50 72.5,87t99.5,55ZM584,782q56,-18 99.5,-55t72.5,-87L638,640q-9,38 -22.5,73.5T584,782ZM170,560h136q-3,-20 -4.5,-39.5T300,480q0,-21 1.5,-40.5T306,400L170,400q-5,20 -7.5,39.5T160,480q0,21 2.5,40.5T170,560ZM386,560h188q3,-20 4.5,-39.5T580,480q0,-21 -1.5,-40.5T574,400L386,400q-3,20 -4.5,39.5T380,480q0,21 1.5,40.5T386,560ZM654,560h136q5,-20 7.5,-39.5T800,480q0,-21 -2.5,-40.5T790,400L654,400q3,20 4.5,39.5T660,480q0,21 -1.5,40.5T654,560ZM638,320h118q-29,-50 -72.5,-87T584,178q18,33 31.5,68.5T638,320ZM404,320h152q-12,-44 -31,-83t-45,-75q-26,36 -45,75t-31,83ZM204,320h118q9,-38 22.5,-73.5T376,178q-56,18 -99.5,55T204,320Z"/>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#FF000000"
android:pathData="M120,840v-80l80,-80v160h-80ZM280,840v-240l80,-80v320h-80ZM440,840v-320l80,81v239h-80ZM600,840v-239l80,-80v319h-80ZM760,840v-400l80,-80v480h-80ZM120,633v-113l280,-280 160,160 280,-280v113L560,513 400,353 120,633Z"/>
</vector>
+9
View File
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#FF000000"
android:pathData="M440,760h80v-167l64,64 56,-57 -160,-160 -160,160 57,56 63,-63v167ZM240,880q-33,0 -56.5,-23.5T160,800v-640q0,-33 23.5,-56.5T240,80h320l240,240v480q0,33 -23.5,56.5T720,880L240,880ZM520,360v-200L240,160v640h480v-440L520,360ZM240,160v200,-200 640,-640Z"/>
</vector>
+2
View File
@@ -1,5 +1,6 @@
[versions] [versions]
agp = "8.1.2" agp = "8.1.2"
compose-settings-ui-m3 = "1.0.3"
junit-junit = "4.13.2" junit-junit = "4.13.2"
kotlin = "1.9.21" kotlin = "1.9.21"
core-ktx = "1.12.0" core-ktx = "1.12.0"
@@ -18,6 +19,7 @@ room = "2.6.1"
voyager = "1.0.0" voyager = "1.0.0"
[libraries] [libraries]
compose-settings-ui-m3 = { module = "com.github.alorma:compose-settings-ui-m3", version.ref = "compose-settings-ui-m3" }
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" } core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
junit = { group = "junit", name = "junit", version.ref = "junit" } junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" } androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" }
+1
View File
@@ -0,0 +1 @@
/build
+55
View File
@@ -0,0 +1,55 @@
@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
plugins {
alias(libs.plugins.androidLibrary)
alias(libs.plugins.kotlinAndroid)
}
android {
namespace = "ru.megboyzz.mpcore"
compileSdk = 33
defaultConfig {
minSdk = 21
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
externalNativeBuild {
cmake {
cppFlags("-std=c++17")
}
}
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
externalNativeBuild {
cmake {
path("src/main/cpp/CMakeLists.txt")
version = "3.22.1"
}
}
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 ru.megboyzz.mpcore
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("ru.megboyzz.mpcore.test", appContext.packageName)
}
}
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>
+10
View File
@@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.22.1)
project("mpcore")
add_library(${CMAKE_PROJECT_NAME} SHARED
mpcore.cpp)
target_link_libraries(${CMAKE_PROJECT_NAME}
android
log)
+10
View File
@@ -0,0 +1,10 @@
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_ru_megboyzz_mpcore_NativeLib_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
@@ -0,0 +1,12 @@
package ru.megboyzz.mpcore
class NativeLib {
external fun stringFromJNI(): String
companion object {
init {
System.loadLibrary("mpcore")
}
}
}
@@ -0,0 +1,17 @@
package ru.megboyzz.mpcore
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
@@ -23,3 +23,4 @@ rootProject.name = "NFS Most Wanted"
include(":app") include(":app")
include(":devmenu") include(":devmenu")
include(":devmenu:domain") include(":devmenu:domain")
include(":mpcore")