diff --git a/app/src/main/java/com/ea/ironmonkey/GameActivity.kt b/app/src/main/java/com/ea/ironmonkey/GameActivity.kt index 1948529..0098c8f 100644 --- a/app/src/main/java/com/ea/ironmonkey/GameActivity.kt +++ b/app/src/main/java/com/ea/ironmonkey/GameActivity.kt @@ -5,38 +5,25 @@ import android.app.AlertDialog import android.content.DialogInterface import android.content.Intent import android.content.pm.ActivityInfo -import android.graphics.BitmapFactory import android.hardware.SensorManager -import android.hardware.input.InputManager import android.media.AudioManager import android.opengl.GLES20 -import android.opengl.GLUtils import android.os.Build import android.os.Bundle import android.os.PowerManager import android.os.PowerManager.WakeLock import android.util.DisplayMetrics import android.util.Log -import android.view.InputDevice import android.view.KeyEvent import android.view.MotionEvent import android.view.inputmethod.InputMethodManager import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent import androidx.lifecycle.Lifecycle -import com.bda.controller.StateEvent import com.ea.EAIO.EAIO import com.ea.EAMIO.StorageDirectory import com.ea.easp.EASPHandler import com.ea.games.nfs13_na.BuildConfig -import com.ea.games.nfs13_na.R import com.ea.nimble.ApplicationLifecycle -import com.megboyzz.actionForAllControllers -import com.megboyzz.androidKeyEventToBda -import com.megboyzz.androidMotionEventToBda -import com.megboyzz.createConnectEvent -import com.megboyzz.createDisconnectEvent -import com.megboyzz.registerControllerHandlers import java.io.BufferedReader import java.io.File import java.io.FileReader @@ -299,23 +286,20 @@ class GameActivity : ComponentActivity(), DrawFrameListener { ApplicationLifecycle.onActivityResult(requestCode, resultCode, data, this) } - override fun onBackPressed() { - //ApplicationLifecycle.onBackPressed() - } - public override fun onCreate(bundle: Bundle?) { super.onCreate(bundle) - registerControllerHandlers( + MogaController.registerControllerHandlers( + context = baseContext, onInputDeviceAdded = { Log.i("Controller", "controller is connected!") - val event = createConnectEvent(it) + val event = MogaController.createConnectEvent(it) MogaController.nativeOnStateEvent(event) }, onInputDeviceRemoved = { Log.i("Controller", "controller is disconnected!") - val event = createDisconnectEvent(it) + val event = MogaController.createDisconnectEvent(it) MogaController.nativeOnStateEvent(event) }, onInputDeviceChanged = { @@ -393,7 +377,8 @@ class GameActivity : ComponentActivity(), DrawFrameListener { override fun dispatchGenericMotionEvent(ev: MotionEvent?): Boolean { Log.i("GameActivity", ev.toString()) - MogaController.onMotionEvent(androidMotionEventToBda(ev!!)) + if (MogaController.deviceIsController(ev?.deviceId!!)) + MogaController.onMotionEvent(MogaController.androidMotionEventToBda(ev)) return super.dispatchGenericMotionEvent(ev) } @@ -401,7 +386,8 @@ class GameActivity : ComponentActivity(), DrawFrameListener { @SuppressLint("RestrictedApi") override fun dispatchKeyEvent(event: KeyEvent?): Boolean { - MogaController.onKeyEvent(androidKeyEventToBda(event!!)) + if (MogaController.deviceIsController(event?.deviceId!!)) + MogaController.onKeyEvent(MogaController.androidKeyEventToBda(event)) return super.dispatchKeyEvent(event) } @@ -409,8 +395,6 @@ class GameActivity : ComponentActivity(), DrawFrameListener { override fun onDrawFrame(gl10: GL10) { - //InputDevice.getDeviceIds() - //InputDevice.getDevice(0).sources Log.d(this.localClassName, "onDrawFrame state=$state") var isStarted = false when (state) { @@ -522,8 +506,8 @@ class GameActivity : ComponentActivity(), DrawFrameListener { gameGLSurfaceView.onResume() ApplicationLifecycle.onActivityResume(this) nativeOnResume() - actionForAllControllers { - val event = createConnectEvent(it) + MogaController.doForAllControllers { + val event = MogaController.createConnectEvent(it) MogaController.nativeOnStateEvent(event) } } diff --git a/app/src/main/java/com/ea/ironmonkey/MogaController.kt b/app/src/main/java/com/ea/ironmonkey/MogaController.kt index ca39b51..6c15ea9 100644 --- a/app/src/main/java/com/ea/ironmonkey/MogaController.kt +++ b/app/src/main/java/com/ea/ironmonkey/MogaController.kt @@ -1,5 +1,10 @@ package com.ea.ironmonkey +import android.content.Context +import android.hardware.input.InputManager +import android.util.Log +import android.view.InputDevice +import androidx.activity.ComponentActivity import com.bda.controller.ControllerListener import com.bda.controller.KeyEvent import com.bda.controller.MotionEvent @@ -19,4 +24,94 @@ object MogaController : ControllerListener { override fun onStateEvent(stateEvent: StateEvent) { nativeOnStateEvent(stateEvent) } + + fun doForAllControllers( + action: (Int) -> Unit + ){ + val deviceIds = InputDevice.getDeviceIds() + + deviceIds.forEach { + if(deviceIsController(it)) action(it) + } + } + + infix fun Int.isFlag(flag: Int) = this and flag == flag + + fun deviceIsController(deviceId: Int): Boolean { + val inputDevice = InputDevice.getDevice(deviceId) ?: return false + Log.i("Controller", inputDevice.toString()) + + val b = (inputDevice.sources isFlag InputDevice.SOURCE_GAMEPAD) && + (inputDevice.sources isFlag InputDevice.SOURCE_JOYSTICK) + + Log.i("Controller", b.toString()) + + if(inputDevice.id == -1) return false + + return b + } + + fun registerControllerHandlers( + context: Context, + onInputDeviceAdded: (Int) -> Unit, + onInputDeviceRemoved: (Int) -> Unit, + onInputDeviceChanged: (Int) -> Unit = {}, + ){ + + val inputManager = context.getSystemService(ComponentActivity.INPUT_SERVICE) as InputManager + + inputManager.registerInputDeviceListener(object : InputManager.InputDeviceListener { + + var lastControllerId: Int = 0; + + override fun onInputDeviceAdded(deviceId: Int) { + if(deviceIsController(deviceId)) { + lastControllerId = deviceId + onInputDeviceAdded(deviceId) + } + } + + override fun onInputDeviceRemoved(deviceId: Int) { + + if(deviceIsController(deviceId) || lastControllerId == deviceId) onInputDeviceRemoved(deviceId) + } + + override fun onInputDeviceChanged(deviceId: Int) { + if(deviceIsController(deviceId)) onInputDeviceChanged(deviceId) + } + }, null) + + } + + fun createDisconnectEvent(deviceId: Int) = StateEvent( + System.currentTimeMillis(), + deviceId, + StateEvent.STATE_UNKNOWN, + StateEvent.ACTION_DISCONNECTED + ) + + fun createConnectEvent(deviceId: Int) = StateEvent( + System.currentTimeMillis(), + deviceId, + StateEvent.STATE_CONNECTION, + StateEvent.ACTION_CONNECTED + ) + + fun androidKeyEventToBda(keyEvent: android.view.KeyEvent) = KeyEvent( + keyEvent.eventTime, + keyEvent.deviceId, + keyEvent.keyCode, + keyEvent.action + ) + + fun androidMotionEventToBda(motionEvent: android.view.MotionEvent) = MotionEvent( + motionEvent.eventTime, + motionEvent.deviceId, + motionEvent.x, + motionEvent.y, + motionEvent.rawX, + motionEvent.rawY, + motionEvent.xPrecision, + motionEvent.yPrecision + ) } diff --git a/app/src/main/java/com/megboyzz/Util.kt b/app/src/main/java/com/megboyzz/Util.kt deleted file mode 100644 index 12ff2ac..0000000 --- a/app/src/main/java/com/megboyzz/Util.kt +++ /dev/null @@ -1,91 +0,0 @@ -package com.megboyzz - -import android.content.Context -import android.hardware.input.InputManager -import android.view.InputDevice -import androidx.activity.ComponentActivity -import com.bda.controller.KeyEvent -import com.bda.controller.MotionEvent -import android.view.KeyEvent as androidKeyEvent -import android.view.MotionEvent as androidMotionEvent -import com.bda.controller.StateEvent - - -fun actionForAllControllers( - action: (Int) -> Unit -){ - val deviceIds = InputDevice.getDeviceIds() - deviceIds.forEach { - if(deviceIsController(it)) action(it) - } -} - -fun deviceIsController(deviceId: Int): Boolean { - val inputDevice = InputDevice.getDevice(deviceId) ?: return false - - return (inputDevice.sources and InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD || - (inputDevice.sources and InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK -} - -fun Context.registerControllerHandlers( - onInputDeviceAdded: (Int) -> Unit, - onInputDeviceRemoved: (Int) -> Unit, - onInputDeviceChanged: (Int) -> Unit = {}, -){ - - val inputManager = getSystemService(ComponentActivity.INPUT_SERVICE) as InputManager - - inputManager.registerInputDeviceListener(object : InputManager.InputDeviceListener { - - var lastControllerId: Int = 0; - - override fun onInputDeviceAdded(deviceId: Int) { - if(deviceIsController(deviceId)) { - lastControllerId = deviceId - onInputDeviceAdded(deviceId) - } - } - - override fun onInputDeviceRemoved(deviceId: Int) { - - if(deviceIsController(deviceId) || lastControllerId == deviceId) onInputDeviceRemoved(deviceId) - } - - override fun onInputDeviceChanged(deviceId: Int) { - if(deviceIsController(deviceId)) onInputDeviceChanged(deviceId) - } - }, null) - -} - -fun createDisconnectEvent(deviceId: Int) = StateEvent( - System.currentTimeMillis(), - deviceId, - StateEvent.STATE_UNKNOWN, - StateEvent.ACTION_DISCONNECTED - ) - -fun createConnectEvent(deviceId: Int) = StateEvent( - System.currentTimeMillis(), - deviceId, - StateEvent.STATE_CONNECTION, - StateEvent.ACTION_CONNECTED -) - -fun androidKeyEventToBda(keyEvent: androidKeyEvent) = KeyEvent( - keyEvent.eventTime, - keyEvent.deviceId, - keyEvent.keyCode, - keyEvent.action -) - -fun androidMotionEventToBda(motionEvent: androidMotionEvent) = MotionEvent( - motionEvent.eventTime, - motionEvent.deviceId, - motionEvent.x, - motionEvent.y, - motionEvent.rawX, - motionEvent.rawY, - motionEvent.xPrecision, - motionEvent.yPrecision -) \ No newline at end of file diff --git a/devmenu/build.gradle.kts b/devmenu/build.gradle.kts index 572b182..8aa93bd 100644 --- a/devmenu/build.gradle.kts +++ b/devmenu/build.gradle.kts @@ -71,6 +71,11 @@ dependencies { debugImplementation(libs.ui.tooling) debugImplementation(libs.ui.test.manifest) + implementation(libs.voyager.navigator) + implementation(libs.voyager.bottomSheetNavigator) + implementation(libs.voyager.transitions) + implementation(libs.voyager.screenModel) + implementation(libs.dagger) kapt(libs.dagger.compiler) implementation(libs.room) diff --git a/devmenu/src/main/java/com/megboyzz/devmenu/MainActivity.kt b/devmenu/src/main/java/com/megboyzz/devmenu/MainActivity.kt index 5873f9b..56b508b 100644 --- a/devmenu/src/main/java/com/megboyzz/devmenu/MainActivity.kt +++ b/devmenu/src/main/java/com/megboyzz/devmenu/MainActivity.kt @@ -4,6 +4,7 @@ import android.content.Intent import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import cafe.adriel.voyager.navigator.bottomSheet.BottomSheetNavigator import com.megboyzz.devmenu.ui.components.MainScaffold class MainActivity : ComponentActivity() { @@ -30,6 +31,11 @@ class MainActivity : ComponentActivity() { } setContent { + + BottomSheetNavigator { + + } + MainScaffold( onSettingsClick = { /*TODO*/ }, onSearchValueChanged = {}, diff --git a/devmenu/src/main/java/com/megboyzz/devmenu/ui/components/AnimationSpec.kt b/devmenu/src/main/java/com/megboyzz/devmenu/ui/components/AnimationSpec.kt new file mode 100644 index 0000000..c6aac93 --- /dev/null +++ b/devmenu/src/main/java/com/megboyzz/devmenu/ui/components/AnimationSpec.kt @@ -0,0 +1,18 @@ +package com.megboyzz.devmenu.ui.components + +import androidx.compose.animation.core.tween +import androidx.compose.animation.expandIn + +import androidx.compose.animation.shrinkOut + +import androidx.compose.ui.Alignment + +val enter = expandIn( + expandFrom = Alignment.CenterStart, + animationSpec = tween(250) +) + +val exit = shrinkOut( + shrinkTowards = Alignment.CenterStart, + animationSpec = tween(250) +) \ No newline at end of file diff --git a/devmenu/src/main/java/com/megboyzz/devmenu/ui/components/ContextMenu.kt b/devmenu/src/main/java/com/megboyzz/devmenu/ui/components/ContextMenu.kt index 444eabf..cad40bd 100644 --- a/devmenu/src/main/java/com/megboyzz/devmenu/ui/components/ContextMenu.kt +++ b/devmenu/src/main/java/com/megboyzz/devmenu/ui/components/ContextMenu.kt @@ -1,26 +1,20 @@ package com.megboyzz.devmenu.ui.components +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.KeyboardArrowRight -import androidx.compose.material.icons.outlined.Star import androidx.compose.material3.Button import androidx.compose.material3.Divider -import androidx.compose.material3.Icon -import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -33,7 +27,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.megboyzz.devmenu.R @@ -68,7 +61,7 @@ fun CoreMenuPosition( ) { Row( verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(16.dp), + horizontalArrangement = Arrangement.spacedBy(20.dp), ) { Image( painter = icon, @@ -87,11 +80,11 @@ fun FsElementInContextMenu( Row( verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(12.dp) + horizontalArrangement = Arrangement.spacedBy(20.dp) ) { val modifier = Modifier - .size(32.dp) + .size(36.dp) .clip(RoundedCornerShape(10.dp)) .shadow(elevation = 6.dp) @@ -108,55 +101,92 @@ fun FileContextMenu( name: String, type: ElementType, onRemoveElement: () -> Unit, - onUpdateType: (ElementType) -> Unit + onUpdateType: (ElementType) -> ElementType? ) { var innerType by remember { mutableStateOf(type) } var isMain by remember { mutableStateOf(true) } + CoreFileContextMenu { - Column(verticalArrangement = Arrangement.spacedBy(20.dp)) { - FsElementInContextMenu(name = name, type = type) - Divider() - if (isMain) { + FsElementInContextMenu(name = name, type = type) + Divider() + AnimatedBoxForContextMenuContent(isVisible = isMain){ + AnimatedBoxForContextMenuContent(isVisible = innerType == ElementType.isFile) { ClickableMenuPosition( name = "Заменить", icon = R.drawable.star.asPainter(), onClick = { - innerType = ElementType.isReplaced - onUpdateType(innerType) + innerType = onUpdateType(ElementType.isReplaced) ?: ElementType.isReplaced } ) ClickableMenuPosition( name = "Скрыть для игры", icon = R.drawable.hide.asPainter(), onClick = { - innerType = ElementType.isHidden - + innerType = onUpdateType(ElementType.isHidden) ?: ElementType.isHidden } ) + } + AnimatedBoxForContextMenuContent(isVisible = innerType == ElementType.isHidden) { ClickableMenuPosition( - name = "Свойства", - icon = R.drawable.info.asPainter(), - onClick = {} - ) - ClickableMenuPosition( - name = "Удалить", - icon = R.drawable.trash.asPainter(), - onClick = { isMain = !isMain } - ) - }else{ - ClickableMenuPosition( - name = "Удалить", - icon = R.drawable.trash.asPainter(), - withEnd = false - ) - RemoveFileButtonsGroup( - onDelete = onRemoveElement, - onCancel = { isMain =! isMain } + name = "Показать игре", + icon = R.drawable.unhide.asPainter(), + onClick = { + innerType = onUpdateType(ElementType.isFile) ?: ElementType.isFile + } ) } + AnimatedBoxForContextMenuContent(isVisible = innerType == ElementType.isReplaced) { + ClickableMenuPosition( + name = "Воостановить", + icon = R.drawable.star.asPainter(), + onClick = { + innerType = onUpdateType(ElementType.isFile) ?: ElementType.isFile + } + ) + } + + ClickableMenuPosition( + name = "Свойства", + icon = R.drawable.info.asPainter(), + onClick = {} + ) + ClickableMenuPosition( + name = "Удалить", + icon = R.drawable.trash.asPainter(), + onClick = { isMain = !isMain } + ) } + + AnimatedBoxForContextMenuContent(isVisible = !isMain) { + ClickableMenuPosition( + name = "Удалить", + icon = R.drawable.trash.asPainter(), + withEnd = false + ) + RemoveFileButtonsGroup( + onDelete = onRemoveElement, + onCancel = { isMain = !isMain } + ) + } + } +} + +@Composable +fun AnimatedBoxForContextMenuContent( + isVisible: Boolean, + content: @Composable ColumnScope.() -> Unit +) { + AnimatedVisibility( + visible = isVisible, + enter = enter, + exit = exit + ) { + Column( + verticalArrangement = Arrangement.spacedBy(12.dp), + content = content + ) } } @@ -174,31 +204,6 @@ fun RemoveFileButtonsGroup( } } -@Preview -@Composable -fun TwoButtonsRow() { - Surface( - modifier = Modifier.fillMaxWidth() - ) { - Row( - modifier = Modifier.fillMaxWidth(), - ) { - Button( - onClick = { /* Действие для первой кнопки */ }, - modifier = Modifier.weight(1f) // Каждая кнопка занимает равное количество места (50%) - ) { - // Ваш текст и стиль для первой кнопки - } - - Button( - onClick = { /* Действие для второй кнопки */ }, - modifier = Modifier.weight(1f) // Каждая кнопка занимает равное количество места (50%) - ) { - // Ваш текст и стиль для второй кнопки - } - } - } -} @Composable fun RemoveButton( @@ -206,38 +211,46 @@ fun RemoveButton( modifier: Modifier = Modifier, onClick: () -> Unit ) { - Row() { - Button( - onClick = onClick, - shape = RoundedCornerShape(10.dp), - modifier = modifier - ) { - Text(text = text) - } + Button( + onClick = onClick, + shape = RoundedCornerShape(10.dp), + modifier = modifier + ) { + Text(text = text) } } @Composable fun CoreFileContextMenu( - content: @Composable BoxScope.() -> Unit + content: @Composable () -> Unit ) { + + val shape = RoundedCornerShape(topStart = 10.dp, topEnd = 10.dp) + Box( modifier = Modifier .wrapContentHeight() .fillMaxWidth() //TODO отвязать от цвета - .background(Color.White) - .clip(RoundedCornerShape(10.dp)), + .clip(shape) + .background(Color.White), contentAlignment = Alignment.TopCenter ) { - + Image( + painter = R.drawable.clip.asPainter(), + contentDescription = null, + modifier = Modifier.padding(top = 8.dp) + ) Box( modifier = Modifier.padding( - horizontal = 32.dp, - vertical = 24.dp + horizontal = 28.dp, + vertical = 32.dp ), - content = content - ) + ){ + Column(verticalArrangement = Arrangement.spacedBy(20.dp)) { + content() + } + } } } @@ -250,7 +263,7 @@ fun ClicablePreview() { name = "my_file.txt", type = ElementType.isFile, onRemoveElement = {}, - onUpdateType = {} + onUpdateType = {null} ) } \ No newline at end of file diff --git a/devmenu/src/main/java/com/megboyzz/devmenu/ui/components/Shared.kt b/devmenu/src/main/java/com/megboyzz/devmenu/ui/components/Shared.kt index 87db82e..fd75705 100644 --- a/devmenu/src/main/java/com/megboyzz/devmenu/ui/components/Shared.kt +++ b/devmenu/src/main/java/com/megboyzz/devmenu/ui/components/Shared.kt @@ -44,10 +44,10 @@ fun FsElementIcon( ) { val painter = when(type){ - ElementType.isHidden -> R.drawable.icon_hidden_file.asPainter() - ElementType.isFile -> R.drawable.icon_file.asPainter() - ElementType.isDir -> R.drawable.icon_folder.asPainter() - ElementType.isReplaced -> R.drawable.icon_replaced_file.asPainter() + ElementType.isHidden -> R.drawable.icon_hidden_file.asPainter() + ElementType.isFile -> R.drawable.icon_file.asPainter() + ElementType.isDir -> R.drawable.icon_folder.asPainter() + ElementType.isReplaced -> R.drawable.icon_replaced_file.asPainter() } Image(painter = painter, contentDescription = null, modifier) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 55c051b..1c80f76 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,6 +15,7 @@ org-jetbrains-kotlin-jvm = "1.9.0" ksp = "1.9.21-1.0.15" dagger = "2.46.1" room = "2.6.1" +voyager = "1.0.0" [libraries] core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" } @@ -40,6 +41,11 @@ room = { module = "androidx.room:room-runtime", version.ref = "room" } room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" } room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" } +voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" } +voyager-screenModel = { module = "cafe.adriel.voyager:voyager-screenmodel", version.ref = "voyager" } +voyager-bottomSheetNavigator = { module = "cafe.adriel.voyager:voyager-bottom-sheet-navigator", version.ref = "voyager" } +voyager-transitions = { module = "cafe.adriel.voyager:voyager-transitions", version.ref = "voyager" } + [plugins] androidApplication = { id = "com.android.application", version.ref = "agp" } kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }