Более менее стабильная версия
This commit is contained in:
Generated
+8
@@ -4,6 +4,14 @@
|
|||||||
<selectionStates>
|
<selectionStates>
|
||||||
<SelectionState runConfigName="app">
|
<SelectionState runConfigName="app">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
<DropdownSelection timestamp="2025-08-28T09:42:35.298333191Z">
|
||||||
|
<Target type="DEFAULT_BOOT">
|
||||||
|
<handle>
|
||||||
|
<DeviceId pluginId="Default" identifier="serial=192.168.240.112:5555;connection=1b67fee5" />
|
||||||
|
</handle>
|
||||||
|
</Target>
|
||||||
|
</DropdownSelection>
|
||||||
|
<DialogSelection />
|
||||||
</SelectionState>
|
</SelectionState>
|
||||||
</selectionStates>
|
</selectionStates>
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
Generated
+2
-1
@@ -1,6 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
<uses-configuration android:reqTouchScreen="finger"/>
|
<uses-configuration android:reqTouchScreen="finger"/>
|
||||||
<application
|
<application
|
||||||
android:theme="@style/Theme.AppCompat.Light"
|
android:theme="@style/Theme.AppCompat.NoActionBar"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:icon="@mipmap/adaptive_icon"
|
android:icon="@mipmap/adaptive_icon"
|
||||||
android:screenOrientation="userLandscape"
|
android:screenOrientation="userLandscape"
|
||||||
|
|||||||
@@ -1,16 +1,25 @@
|
|||||||
package com.ea.EAIO;
|
package com.ea.EAIO
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity
|
||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager
|
||||||
import android.os.Environment;
|
import android.os.Environment
|
||||||
|
|
||||||
|
object EAIO {
|
||||||
|
@JvmStatic
|
||||||
|
external fun Shutdown()
|
||||||
|
|
||||||
public class EAIO {
|
private external fun StartupNativeImpl(
|
||||||
public static native void Shutdown();
|
assetManager: AssetManager?,
|
||||||
|
dataPath: String?,
|
||||||
|
filesDirPath: String?,
|
||||||
|
externalPath: String?
|
||||||
|
)
|
||||||
|
|
||||||
private static native void StartupNativeImpl(AssetManager assetManager, String str, String str2, String str3);
|
@JvmStatic
|
||||||
|
fun Startup(activity: Activity) = StartupNativeImpl(
|
||||||
public static void Startup(Activity activity) {
|
activity.assets,
|
||||||
StartupNativeImpl(activity.getAssets(), Environment.getDataDirectory().getAbsolutePath(), activity.getFilesDir().getAbsolutePath(), Environment.getExternalStorageDirectory().getAbsolutePath());
|
Environment.getDataDirectory().absolutePath,
|
||||||
}
|
activity.filesDir.absolutePath,
|
||||||
|
Environment.getExternalStorageDirectory().absolutePath
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,42 +1,52 @@
|
|||||||
package com.ea.EAMIO;
|
package com.ea.EAMIO
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.annotation.SuppressLint
|
||||||
import android.os.Environment;
|
import android.app.Activity
|
||||||
|
import android.os.Environment
|
||||||
|
|
||||||
|
//На методы есть ссылка из нативного кода но они не вызываются и не несут полезной нагрузки
|
||||||
|
@SuppressLint("StaticFieldLeak")
|
||||||
|
object StorageDirectory {
|
||||||
|
@JvmStatic
|
||||||
|
var sActivity: Activity? = null
|
||||||
|
|
||||||
public class StorageDirectory {
|
private external fun ShutdownNativeImpl()
|
||||||
public static Activity sActivity;
|
|
||||||
|
|
||||||
private static native void ShutdownNativeImpl();
|
private external fun StartupNativeImpl()
|
||||||
|
|
||||||
private static native void StartupNativeImpl();
|
@JvmStatic
|
||||||
|
fun Startup(activity: Activity) {
|
||||||
public static void Startup(Activity activity) {
|
sActivity = activity
|
||||||
sActivity = activity;
|
StartupNativeImpl()
|
||||||
StartupNativeImpl();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Shutdown() {
|
@JvmStatic
|
||||||
ShutdownNativeImpl();
|
fun Shutdown() {
|
||||||
|
ShutdownNativeImpl()
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String GetInternalStorageDirectory() {
|
@JvmStatic
|
||||||
return sActivity.getFilesDir().getAbsolutePath();
|
fun GetInternalStorageDirectory(): String {
|
||||||
|
return sActivity?.filesDir?.absolutePath ?: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String GetPrimaryExternalStorageDirectory() {
|
@JvmStatic
|
||||||
return sActivity.getExternalFilesDir(null).getAbsolutePath();
|
fun GetPrimaryExternalStorageDirectory(): String {
|
||||||
|
return sActivity?.getExternalFilesDir(null)?.absolutePath ?: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String GetPrimaryExternalStorageDirectoryRoot() {
|
@JvmStatic
|
||||||
return Environment.getExternalStorageDirectory().getAbsolutePath();
|
fun GetPrimaryExternalStorageDirectoryRoot(): String {
|
||||||
|
return Environment.getExternalStorageDirectory().absolutePath
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int GetPrimaryExternalStorageState() {
|
@JvmStatic
|
||||||
String externalStorageState = Environment.getExternalStorageState();
|
fun GetPrimaryExternalStorageState(): Int {
|
||||||
if ("mounted".equals(externalStorageState)) {
|
val externalStorageState = Environment.getExternalStorageState()
|
||||||
return 2;
|
return when {
|
||||||
|
Environment.MEDIA_MOUNTED == externalStorageState -> 2
|
||||||
|
Environment.MEDIA_MOUNTED_READ_ONLY == externalStorageState -> 1
|
||||||
|
else -> 0
|
||||||
}
|
}
|
||||||
return "mounted_ro".equals(externalStorageState) ? 1 : 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,64 +1,58 @@
|
|||||||
package com.ea.ironmonkey;
|
package com.ea.ironmonkey
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
import android.content.res.AssetManager
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint
|
||||||
import android.graphics.PorterDuff;
|
import android.graphics.PorterDuff
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface
|
||||||
import android.util.Log;
|
import androidx.core.graphics.createBitmap
|
||||||
|
import com.ea.ironmonkey.GameActivityMain.Companion.mAssetLocationType
|
||||||
|
import com.ea.ironmonkey.domain.AssetLocationType
|
||||||
|
|
||||||
import nfs.mod.mpcore.MultiplayerCore;
|
class BitmapGraphics(width: Int, height: Int) {
|
||||||
|
val bitmap = createBitmap(width, height)
|
||||||
|
val canvas = Canvas()
|
||||||
|
|
||||||
|
init {
|
||||||
public class BitmapGraphics {
|
canvas.setBitmap(bitmap)
|
||||||
private Bitmap bitmap;
|
|
||||||
private Canvas canvas = new Canvas();
|
|
||||||
|
|
||||||
public BitmapGraphics(int i, int i2) {
|
|
||||||
this.bitmap = Bitmap.createBitmap(i, i2, Bitmap.Config.ARGB_8888);
|
|
||||||
this.canvas.setBitmap(this.bitmap);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bitmap getBitmap() {
|
fun clear() {
|
||||||
return this.bitmap;
|
canvas.drawColor(0, PorterDuff.Mode.CLEAR)
|
||||||
}
|
}
|
||||||
|
|
||||||
public Canvas getCanvas() {
|
fun drawString(paint: Paint, text: String, x: Int, y: Int) = canvas.drawText(text, x.toFloat(), y.toFloat(), paint)
|
||||||
return this.canvas;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clear() {
|
companion object {
|
||||||
this.canvas.drawColor(0, PorterDuff.Mode.CLEAR);
|
private fun createPaint(typeface: Typeface?, f: Float): Paint {
|
||||||
}
|
val paint = Paint()
|
||||||
|
paint.setTypeface(typeface)
|
||||||
|
paint.textSize = f
|
||||||
|
paint.setColor(-1)
|
||||||
|
paint.isAntiAlias = true
|
||||||
|
return paint
|
||||||
|
}
|
||||||
|
|
||||||
private static Paint createPaint(Typeface typeface, float f) {
|
@JvmStatic
|
||||||
Paint paint = new Paint();
|
fun createPaintFromFamilyName(familyName: String, f: Float): Paint {
|
||||||
paint.setTypeface(typeface);
|
return createPaint(Typeface.create(familyName, Typeface.NORMAL), f)
|
||||||
paint.setTextSize(f);
|
}
|
||||||
paint.setColor(-1);
|
|
||||||
paint.setAntiAlias(true);
|
|
||||||
return paint;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Paint createPaintFromFamilyName(String str, float f) {
|
@JvmStatic
|
||||||
return createPaint(Typeface.create(str, 0), f);
|
fun createPaintFromFile(str: String, f: Float): Paint {
|
||||||
}
|
return createPaint(internalCreateFromFile(str), f)
|
||||||
|
}
|
||||||
|
|
||||||
public static Paint createPaintFromFile(String str, float f) {
|
private fun internalCreateFromFile(str: String): Typeface? {
|
||||||
return createPaint(internalCreateFromFile(str), f);
|
var str = str
|
||||||
}
|
/*if (mAssetLocationType != AssetLocationType.EXTERNAL) {
|
||||||
|
return Typeface.createFromFile(str);
|
||||||
private static Typeface internalCreateFromFile(String str) {
|
}*/
|
||||||
if (GameActivityMain.instance.useAssetsFileSystem()) {
|
|
||||||
if (str.startsWith("/")) {
|
if (str.startsWith("/")) {
|
||||||
str = str.substring(1);
|
str = str.substring(1);
|
||||||
}
|
}
|
||||||
return Typeface.createFromAsset(GameActivityMain.instance.getAssetManager(), str);
|
return Typeface.createFromAsset(GameActivityMain.assetManager, str);
|
||||||
}
|
|
||||||
return Typeface.createFromFile(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void drawString(Paint paint, String str, int x, int y) {
|
}
|
||||||
this.canvas.drawText(str, x, y, paint);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package com.ea.ironmonkey;
|
package com.ea.ironmonkey
|
||||||
|
|
||||||
import javax.microedition.khronos.opengles.GL10;
|
import javax.microedition.khronos.opengles.GL10
|
||||||
|
|
||||||
|
interface DrawFrameListener {
|
||||||
public interface DrawFrameListener {
|
fun onDrawFrame(gl10: GL10?)
|
||||||
void onDrawFrame(GL10 gl10);
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,46 +1,30 @@
|
|||||||
package com.ea.ironmonkey;
|
package com.ea.ironmonkey
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
|
||||||
|
|
||||||
public class Log {
|
object Log {
|
||||||
private static boolean enable;
|
private var enable = false
|
||||||
|
|
||||||
public static void setEnable(boolean z) {
|
fun setEnable(enabled: Boolean) {
|
||||||
enable = z;
|
enable = enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void i(String str, String str2) {
|
@JvmStatic
|
||||||
if (enable) {
|
fun i(tag: String, msg: String) = if (enable) Log.i(tag, msg) else Unit
|
||||||
android.util.Log.i(str, str2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void e(String str, String str2) {
|
@JvmStatic
|
||||||
if (enable) {
|
fun e(tag: String, msg: String) = if (enable) Log.e(tag, msg) else Unit
|
||||||
android.util.Log.e(str, str2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void w(String str, String str2) {
|
@JvmStatic
|
||||||
if (enable) {
|
fun w(tag: String, msg: String) = if (enable) Log.w(tag, msg) else Unit
|
||||||
android.util.Log.w(str, str2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void d(String str, String str2) {
|
@JvmStatic
|
||||||
if (enable) {
|
fun d(tag: String, msg: String) = if (enable) Log.d(tag, msg) else Unit
|
||||||
android.util.Log.d(str, str2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void v(String str, String str2) {
|
@JvmStatic
|
||||||
if (enable) {
|
fun v(tag: String, msg: String) = if (enable) Log.v(tag, msg) else Unit
|
||||||
android.util.Log.v(str, str2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void e(String str, String str2, Exception exc) {
|
@JvmStatic
|
||||||
if (enable) {
|
fun e(tag: String, msg: String?, exc: Exception?) = if (enable) Log.e(tag, msg, exc) else Unit
|
||||||
android.util.Log.e(str, str2, exc);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +1,117 @@
|
|||||||
package com.ea.ironmonkey;
|
package com.ea.ironmonkey
|
||||||
|
|
||||||
import com.bda.controller.ControllerListener;
|
import android.content.Context
|
||||||
import com.bda.controller.KeyEvent;
|
import android.hardware.input.InputManager
|
||||||
import com.bda.controller.MotionEvent;
|
import android.util.Log
|
||||||
import com.bda.controller.StateEvent;
|
import android.view.InputDevice
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import com.bda.controller.ControllerListener
|
||||||
|
import com.bda.controller.KeyEvent
|
||||||
|
import com.bda.controller.MotionEvent
|
||||||
|
import com.bda.controller.StateEvent
|
||||||
|
|
||||||
|
object MogaController : ControllerListener {
|
||||||
|
external fun nativeOnKeyEvent(keyEvent: KeyEvent?)
|
||||||
|
|
||||||
public class MogaController implements ControllerListener {
|
external fun nativeOnMotionEvent(motionEvent: MotionEvent?)
|
||||||
native void nativeOnKeyEvent(KeyEvent keyEvent);
|
|
||||||
|
|
||||||
native void nativeOnMotionEvent(MotionEvent motionEvent);
|
external fun nativeOnStateEvent(stateEvent: StateEvent?)
|
||||||
|
|
||||||
native void nativeOnStateEvent(StateEvent stateEvent);
|
override fun onKeyEvent(keyEvent: KeyEvent?) {
|
||||||
|
nativeOnKeyEvent(keyEvent)
|
||||||
@Override // com.bda.controller.ControllerListener
|
|
||||||
public void onKeyEvent(KeyEvent keyEvent) {
|
|
||||||
nativeOnKeyEvent(keyEvent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override // com.bda.controller.ControllerListener
|
override fun onMotionEvent(motionEvent: MotionEvent?) {
|
||||||
public void onMotionEvent(MotionEvent motionEvent) {
|
nativeOnMotionEvent(motionEvent)
|
||||||
nativeOnMotionEvent(motionEvent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override // com.bda.controller.ControllerListener
|
override fun onStateEvent(stateEvent: StateEvent?) {
|
||||||
public void onStateEvent(StateEvent stateEvent) {
|
nativeOnStateEvent(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
|
||||||
|
|
||||||
|
if(inputDevice.id == -1) return false
|
||||||
|
|
||||||
|
return (inputDevice.sources isFlag InputDevice.SOURCE_GAMEPAD) &&
|
||||||
|
(inputDevice.sources isFlag InputDevice.SOURCE_JOYSTICK)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
package com.ea.ironmonkey;
|
package com.ea.ironmonkey
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context
|
||||||
|
|
||||||
|
internal object ObbHelper {
|
||||||
class ObbHelper {
|
@JvmStatic
|
||||||
ObbHelper() {
|
fun getObbFileName(context: Context, versionCode: Int) = "main.$versionCode.${context.packageName}.obb"
|
||||||
}
|
|
||||||
|
|
||||||
public static String getObbFileName(Context context, int i) {
|
|
||||||
return "main." + i + "." + context.getPackageName() + ".obb";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,182 +1,186 @@
|
|||||||
package com.ea.ironmonkey;
|
package com.ea.ironmonkey
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.provider.Settings
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
|
||||||
import android.Manifest;
|
class PermissionsActivity : AppCompatActivity() {
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.provider.Settings;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.KeyEvent;
|
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
companion object {
|
||||||
import androidx.core.app.ActivityCompat;
|
private const val SETTINGS_REQUEST_CODE = 100
|
||||||
import androidx.core.content.ContextCompat;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.widget.Toast;
|
|
||||||
import androidx.activity.result.ActivityResultLauncher;
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class PermissionsActivity extends AppCompatActivity {
|
|
||||||
private static final int SETTINGS_REQUEST_CODE = 100;
|
|
||||||
private final ActivityResultLauncher<String[]> requestPermissionLauncher =
|
|
||||||
registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), result -> {
|
|
||||||
if (areAllPermissionsGranted()) {
|
|
||||||
initActivity();
|
|
||||||
} else {
|
|
||||||
handlePermissionsDenied();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
checkPermissions();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkPermissions() {
|
private val requestPermissionLauncher: ActivityResultLauncher<Array<String>> =
|
||||||
|
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { result ->
|
||||||
|
if (areAllPermissionsGranted()) {
|
||||||
|
initActivity()
|
||||||
|
} else {
|
||||||
|
handlePermissionsDenied()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
checkPermissions()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkPermissions() {
|
||||||
if (areAllPermissionsGranted()) {
|
if (areAllPermissionsGranted()) {
|
||||||
initActivity();
|
initActivity()
|
||||||
} else {
|
} else {
|
||||||
requestPermissions();
|
requestPermissions()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean areAllPermissionsGranted() {
|
private fun areAllPermissionsGranted(): Boolean {
|
||||||
for (String permission : getRequiredPermissions()) {
|
return getRequiredPermissions().all { permission ->
|
||||||
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
|
ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String[] getRequiredPermissions() {
|
private fun getRequiredPermissions(): Array<String> {
|
||||||
List<String> permissions = new ArrayList<>();
|
val permissions = mutableListOf<String>()
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
|
when {
|
||||||
// Android 9 (Pie) и ниже - нужны оба разрешения
|
Build.VERSION.SDK_INT <= Build.VERSION_CODES.P -> {
|
||||||
permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE);
|
// Android 9 (Pie) и ниже - нужны оба разрешения
|
||||||
permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||||
} else if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
|
permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||||
// Android 10 (Q) - только чтение
|
}
|
||||||
permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE);
|
Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q -> {
|
||||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
// Android 10 (Q) - только чтение
|
||||||
// Android 13+ - новые раздельные разрешения для медиа
|
permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||||
//permissions.add(Manifest.permission.READ_MEDIA_IMAGES);
|
}
|
||||||
//permissions.add(Manifest.permission.READ_MEDIA_VIDEO);
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
// Android 13+ - новые раздельные разрешения для медиа
|
||||||
// Android 14+ - дополнительно для аудио
|
// permissions.add(Manifest.permission.READ_MEDIA_IMAGES)
|
||||||
permissions.add(Manifest.permission.READ_MEDIA_AUDIO);
|
// permissions.add(Manifest.permission.READ_MEDIA_VIDEO)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||||
|
// Android 14+ - дополнительно для аудио
|
||||||
|
//permissions.add(Manifest.permission.READ_MEDIA_AUDIO)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// Android 11-12 - только чтение
|
||||||
|
permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Android 11-12 - только чтение
|
|
||||||
permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
permissions.forEach(e -> {
|
permissions.forEach { permission ->
|
||||||
Log.i("Permissions", e);
|
Log.i("Permissions", permission)
|
||||||
});
|
}
|
||||||
|
|
||||||
return permissions.toArray(new String[0]);
|
return permissions.toTypedArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
private void requestPermissions() {
|
private fun requestPermissions() {
|
||||||
String[] permissions = getRequiredPermissions();
|
val permissions = getRequiredPermissions()
|
||||||
boolean shouldShowRationale = false;
|
val shouldShowRationale = permissions.any { permission ->
|
||||||
|
ActivityCompat.shouldShowRequestPermissionRationale(this, permission)
|
||||||
for (String permission : permissions) {
|
|
||||||
if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) {
|
|
||||||
shouldShowRationale = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldShowRationale) {
|
if (shouldShowRationale) {
|
||||||
showPermissionRationaleDialog();
|
showPermissionRationaleDialog()
|
||||||
} else {
|
} else {
|
||||||
requestPermissionLauncher.launch(permissions);
|
requestPermissionLauncher.launch(permissions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showPermissionRationaleDialog() {
|
private fun showPermissionRationaleDialog() {
|
||||||
new AlertDialog.Builder(this)
|
AlertDialog.Builder(this)
|
||||||
.setTitle("Требуются разрешения")
|
.setTitle("Требуются разрешения")
|
||||||
.setMessage("Для корректной работы приложения необходимы разрешения на доступ к хранилищу. Пожалуйста, предоставьте запрашиваемые разрешения.")
|
.setMessage("Для корректной работы приложения необходимы разрешения на доступ к хранилищу. Пожалуйста, предоставьте запрашиваемые разрешения.")
|
||||||
.setPositiveButton("OK", (dialog, which) -> requestPermissionLauncher.launch(getRequiredPermissions()))
|
.setPositiveButton("OK") { dialog, _ ->
|
||||||
.setNegativeButton("Отмена", (dialog, which) -> finish())
|
dialog.dismiss()
|
||||||
.setCancelable(false)
|
requestPermissionLauncher.launch(getRequiredPermissions())
|
||||||
.setOnKeyListener((dialog, keyCode, event) -> {
|
|
||||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
|
||||||
finish();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
})
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handlePermissionsDenied() {
|
|
||||||
if (areSomePermissionsPermanentlyDenied()) {
|
|
||||||
showPermanentlyDeniedDialog();
|
|
||||||
} else {
|
|
||||||
Toast.makeText(this, "Разрешения необходимы для работы приложения", Toast.LENGTH_SHORT).show();
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean areSomePermissionsPermanentlyDenied() {
|
|
||||||
for (String permission : getRequiredPermissions()) {
|
|
||||||
if (!ActivityCompat.shouldShowRequestPermissionRationale(this, permission) &&
|
|
||||||
ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
.setNegativeButton("Отмена") { dialog, _ ->
|
||||||
|
dialog.dismiss()
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
.setCancelable(false)
|
||||||
|
.setOnKeyListener { _, keyCode, event ->
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||||
|
finish()
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handlePermissionsDenied() {
|
||||||
|
if (areSomePermissionsPermanentlyDenied()) {
|
||||||
|
showPermanentlyDeniedDialog()
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, "Разрешения необходимы для работы приложения", Toast.LENGTH_SHORT).show()
|
||||||
|
finish()
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showPermanentlyDeniedDialog() {
|
private fun areSomePermissionsPermanentlyDenied(): Boolean {
|
||||||
new AlertDialog.Builder(this)
|
return getRequiredPermissions().any { permission ->
|
||||||
.setTitle("Разрешения отклонены")
|
!ActivityCompat.shouldShowRequestPermissionRationale(this, permission) &&
|
||||||
.setMessage("Вы навсегда отклонили некоторые разрешения. Чтобы использовать приложение, предоставьте разрешения в настройках.")
|
ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED
|
||||||
.setPositiveButton("Настройки", (dialog, which) -> openAppSettings())
|
}
|
||||||
.setNegativeButton("Выход", (dialog, which) -> finish())
|
|
||||||
.setCancelable(false)
|
|
||||||
.show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openAppSettings() {
|
private fun showPermanentlyDeniedDialog() {
|
||||||
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
AlertDialog.Builder(this)
|
||||||
intent.setData(Uri.parse("package:" + getPackageName()));
|
.setTitle("Разрешения отклонены")
|
||||||
startActivityForResult(intent, SETTINGS_REQUEST_CODE);
|
.setMessage("Вы навсегда отклонили некоторые разрешения. Чтобы использовать приложение, предоставьте разрешения в настройках.")
|
||||||
|
.setPositiveButton("Настройки") { dialog, _ ->
|
||||||
|
dialog.dismiss()
|
||||||
|
openAppSettings()
|
||||||
|
}
|
||||||
|
.setNegativeButton("Выход") { dialog, _ ->
|
||||||
|
dialog.dismiss()
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
.setCancelable(false)
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private fun openAppSettings() {
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
data = Uri.fromParts("package", packageName, null)
|
||||||
|
}
|
||||||
|
startActivityForResult(intent, SETTINGS_REQUEST_CODE)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
if (requestCode == SETTINGS_REQUEST_CODE) {
|
if (requestCode == SETTINGS_REQUEST_CODE) {
|
||||||
if (areAllPermissionsGranted()) {
|
if (areAllPermissionsGranted()) {
|
||||||
initActivity();
|
initActivity()
|
||||||
} else {
|
} else {
|
||||||
handlePermissionsDenied();
|
handlePermissionsDenied()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initActivity() {
|
private fun initActivity() {
|
||||||
try {
|
try {
|
||||||
startActivity(new Intent(this, GameActivityMain.class));
|
startActivity(Intent(this, GameActivityMain::class.java))
|
||||||
finish();
|
finish()
|
||||||
} catch (Exception e) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace();
|
e.printStackTrace()
|
||||||
Toast.makeText(this, "Ошибка запуска приложения", Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, "Ошибка запуска приложения", Toast.LENGTH_SHORT).show()
|
||||||
finish();
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,57 +1,52 @@
|
|||||||
package com.ea.ironmonkey;
|
package com.ea.ironmonkey
|
||||||
|
|
||||||
import android.opengl.GLSurfaceView;
|
|
||||||
|
|
||||||
|
|
||||||
public class RunLoop {
|
import android.opengl.GLSurfaceView
|
||||||
public static final int STATE_RUNNING = 1;
|
import android.util.Log
|
||||||
public static final int STATE_STOPPED = 0;
|
|
||||||
private GLSurfaceView glSurfaceView;
|
|
||||||
private int state;
|
|
||||||
|
|
||||||
private native void nativeOnRunLoopTick();
|
class RunLoop(private val glSurfaceView: GLSurfaceView) {
|
||||||
|
|
||||||
public RunLoop(GLSurfaceView gLSurfaceView) {
|
companion object {
|
||||||
this.glSurfaceView = gLSurfaceView;
|
private const val TAG = "RunLoop"
|
||||||
updateRenderMode();
|
private const val STATE_STOPPED = 0
|
||||||
|
private const val STATE_RUNNING = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getState() {
|
private var state: Int = STATE_STOPPED
|
||||||
return this.state;
|
|
||||||
|
private external fun nativeOnRunLoopTick()
|
||||||
|
|
||||||
|
val currentState: Int
|
||||||
|
get() = state
|
||||||
|
|
||||||
|
fun start() {
|
||||||
|
setState(STATE_RUNNING)
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
fun stop() {
|
||||||
setState(1);
|
setState(STATE_STOPPED)
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stop() {
|
fun join() {
|
||||||
setState(0);
|
setState(STATE_STOPPED)
|
||||||
}
|
}
|
||||||
|
|
||||||
public void join() {
|
private fun setState(newState: Int) {
|
||||||
setState(0);
|
state = newState
|
||||||
|
updateRenderMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setState(int i) {
|
private fun updateRenderMode() {
|
||||||
this.state = i;
|
Log.v(TAG, "RunLoop.state = $state")
|
||||||
updateRenderMode();
|
when (state) {
|
||||||
}
|
STATE_STOPPED -> glSurfaceView.renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY
|
||||||
|
STATE_RUNNING -> glSurfaceView.renderMode = GLSurfaceView.RENDERMODE_CONTINUOUSLY
|
||||||
private void updateRenderMode() {
|
|
||||||
Log.v("RunLoop", "RunLoop.state = " + this.state);
|
|
||||||
switch (this.state) {
|
|
||||||
case 0:
|
|
||||||
this.glSurfaceView.setRenderMode(0);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
this.glSurfaceView.setRenderMode(1);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onRunLoopTick() {
|
fun onRunLoopTick() {
|
||||||
if (this.state == 1) {
|
if (state == STATE_RUNNING) {
|
||||||
nativeOnRunLoopTick();
|
nativeOnRunLoopTick()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,255 +1,258 @@
|
|||||||
package com.ea.ironmonkey;
|
package com.ea.ironmonkey
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.BitmapFactory
|
||||||
import android.graphics.BitmapFactory;
|
import android.opengl.GLES20
|
||||||
import android.opengl.GLES20;
|
import android.opengl.GLUtils
|
||||||
import android.opengl.GLUtils;
|
import android.util.Log
|
||||||
|
import java.io.IOException
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
import java.nio.ByteOrder
|
||||||
|
import java.nio.FloatBuffer
|
||||||
|
import javax.microedition.khronos.opengles.GL10
|
||||||
|
|
||||||
import java.io.IOException;
|
class SplashScreen(private val activity: Activity) {
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.Buffer;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.ByteOrder;
|
|
||||||
import java.nio.FloatBuffer;
|
|
||||||
import javax.microedition.khronos.opengles.GL10;
|
|
||||||
|
|
||||||
|
companion object {
|
||||||
public class SplashScreen {
|
private const val TAG = "SplashScreen"
|
||||||
private static final String TAG = "SplashScreen";
|
|
||||||
private Activity _activity;
|
|
||||||
private int _attPosition;
|
|
||||||
private int _attSampler;
|
|
||||||
private int _attTexCoord;
|
|
||||||
private int _fragmentShader;
|
|
||||||
private int _program;
|
|
||||||
private int _vertexShader;
|
|
||||||
private FloatBuffer vBuffer;
|
|
||||||
|
|
||||||
// Вершинный шейдер
|
|
||||||
private final String vShaderStr =
|
|
||||||
"attribute vec4 a_position; \n" +
|
|
||||||
"attribute vec2 a_texCoord; \n" +
|
|
||||||
"varying vec2 v_texCoord; \n" +
|
|
||||||
"void main() { \n" +
|
|
||||||
" gl_Position = a_position; \n" +
|
|
||||||
" v_texCoord = a_texCoord; \n" +
|
|
||||||
"} \n";
|
|
||||||
|
|
||||||
// Фрагментный шейдер
|
|
||||||
private final String fShaderStr =
|
|
||||||
"precision mediump float; \n" +
|
|
||||||
"varying vec2 v_texCoord; \n" +
|
|
||||||
"uniform sampler2D s_texture; \n" +
|
|
||||||
"void main() { \n" +
|
|
||||||
" gl_FragColor = texture2D(s_texture, v_texCoord); \n" +
|
|
||||||
"} \n";
|
|
||||||
|
|
||||||
private int[] _textureId = new int[1];
|
|
||||||
|
|
||||||
public SplashScreen(Activity activity) {
|
|
||||||
this._activity = activity;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init(GL10 gl10, int width, int height) {
|
// Вершинный шейдер
|
||||||
|
private val vertexShaderStr = """
|
||||||
|
attribute vec4 a_position;
|
||||||
|
attribute vec2 a_texCoord;
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
void main() {
|
||||||
|
gl_Position = a_position;
|
||||||
|
v_texCoord = a_texCoord;
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
// Фрагментный шейдер
|
||||||
|
private val fragmentShaderStr = """
|
||||||
|
precision mediump float;
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
uniform sampler2D s_texture;
|
||||||
|
void main() {
|
||||||
|
gl_FragColor = texture2D(s_texture, v_texCoord);
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
private var attPosition = 0
|
||||||
|
private var attSampler = 0
|
||||||
|
private var attTexCoord = 0
|
||||||
|
private var fragmentShader = 0
|
||||||
|
private var program = 0
|
||||||
|
private var vertexShader = 0
|
||||||
|
private var vBuffer: FloatBuffer? = null
|
||||||
|
private var textureId = IntArray(1)
|
||||||
|
|
||||||
|
fun init(gl10: GL10?, width: Int, height: Int) {
|
||||||
|
Log.i("splash", "Я родился!")
|
||||||
if (!initRenderer()) {
|
if (!initRenderer()) {
|
||||||
destroy(gl10);
|
destroy(gl10)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Загрузка текстуры
|
// Загрузка текстуры
|
||||||
GLES20.glGenTextures(1, _textureId, 0);
|
GLES20.glGenTextures(1, textureId, 0)
|
||||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, _textureId[0]);
|
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId[0])
|
||||||
|
|
||||||
// Установка параметров текстуры
|
// Установка параметров текстуры
|
||||||
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
|
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR)
|
||||||
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
|
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR)
|
||||||
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
|
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE)
|
||||||
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
|
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE)
|
||||||
|
|
||||||
// Загрузка изображения
|
// Загрузка изображения
|
||||||
Bitmap bitmap = null;
|
val bitmap = try {
|
||||||
try {
|
activity.assets.open("splash.png").use { stream ->
|
||||||
InputStream is = _activity.getAssets().open("splash.png");
|
BitmapFactory.decodeStream(stream)
|
||||||
bitmap = BitmapFactory.decodeStream(is);
|
}
|
||||||
} catch (IOException e) {
|
} catch (e: IOException) {
|
||||||
Log.e(TAG, "Could not load bitmap", e);
|
Log.e(TAG, "Could not load bitmap", e)
|
||||||
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bitmap != null) {
|
if (bitmap != null) {
|
||||||
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
|
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0)
|
||||||
bitmap.recycle();
|
bitmap.recycle()
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "Failed to load bitmap");
|
Log.e(TAG, "Failed to load bitmap")
|
||||||
destroy(gl10);
|
destroy(gl10)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверка ошибок
|
// Проверка ошибок
|
||||||
int error = GLES20.glGetError();
|
val error = GLES20.glGetError()
|
||||||
if (error != GLES20.GL_NO_ERROR) {
|
if (error != GLES20.GL_NO_ERROR) {
|
||||||
Log.e(TAG, "Texture Load GLError: " + error);
|
Log.e(TAG, "Texture Load GLError: $error")
|
||||||
destroy(gl10);
|
destroy(gl10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean initRenderer() {
|
private fun initRenderer(): Boolean {
|
||||||
// Загрузка шейдеров
|
// Загрузка шейдеров
|
||||||
_vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vShaderStr);
|
vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderStr)
|
||||||
_fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fShaderStr);
|
fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderStr)
|
||||||
|
|
||||||
// Создание программы
|
// Создание программы
|
||||||
_program = GLES20.glCreateProgram();
|
program = GLES20.glCreateProgram()
|
||||||
if (_program == 0) {
|
if (program == 0) {
|
||||||
Log.e(TAG, "Failed to create program");
|
Log.e(TAG, "Failed to create program")
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Прикрепление шейдеров
|
// Прикрепление шейдеров
|
||||||
GLES20.glAttachShader(_program, _vertexShader);
|
GLES20.glAttachShader(program, vertexShader)
|
||||||
GLES20.glAttachShader(_program, _fragmentShader);
|
GLES20.glAttachShader(program, fragmentShader)
|
||||||
|
|
||||||
// Линковка программы
|
// Линковка программы
|
||||||
GLES20.glLinkProgram(_program);
|
GLES20.glLinkProgram(program)
|
||||||
|
|
||||||
// Проверка статуса линковки
|
// Проверка статуса линковки
|
||||||
int[] linkStatus = new int[1];
|
val linkStatus = IntArray(1)
|
||||||
GLES20.glGetProgramiv(_program, GLES20.GL_LINK_STATUS, linkStatus, 0);
|
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0)
|
||||||
if (linkStatus[0] != GLES20.GL_TRUE) {
|
if (linkStatus[0] != GLES20.GL_TRUE) {
|
||||||
Log.e(TAG, "Could not link program: " + GLES20.glGetProgramInfoLog(_program));
|
Log.e(TAG, "Could not link program: ${GLES20.glGetProgramInfoLog(program)}")
|
||||||
GLES20.glDeleteProgram(_program);
|
GLES20.glDeleteProgram(program)
|
||||||
_program = 0;
|
program = 0
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Получение атрибутов
|
// Получение атрибутов
|
||||||
_attPosition = GLES20.glGetAttribLocation(_program, "a_position");
|
attPosition = GLES20.glGetAttribLocation(program, "a_position")
|
||||||
_attTexCoord = GLES20.glGetAttribLocation(_program, "a_texCoord");
|
attTexCoord = GLES20.glGetAttribLocation(program, "a_texCoord")
|
||||||
_attSampler = GLES20.glGetUniformLocation(_program, "s_texture");
|
attSampler = GLES20.glGetUniformLocation(program, "s_texture")
|
||||||
|
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private int loadShader(int type, String shaderCode) {
|
private fun loadShader(type: Int, shaderCode: String): Int {
|
||||||
int shader = GLES20.glCreateShader(type);
|
val shader = GLES20.glCreateShader(type)
|
||||||
if (shader == 0) {
|
if (shader == 0) {
|
||||||
Log.e(TAG, "Failed to create shader");
|
Log.e(TAG, "Failed to create shader")
|
||||||
return 0;
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
GLES20.glShaderSource(shader, shaderCode);
|
GLES20.glShaderSource(shader, shaderCode)
|
||||||
GLES20.glCompileShader(shader);
|
GLES20.glCompileShader(shader)
|
||||||
|
|
||||||
// Проверка статуса компиляции
|
// Проверка статуса компиляции
|
||||||
int[] compiled = new int[1];
|
val compiled = IntArray(1)
|
||||||
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
|
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0)
|
||||||
if (compiled[0] == 0) {
|
if (compiled[0] == 0) {
|
||||||
Log.e(TAG, "Could not compile shader: " + GLES20.glGetShaderInfoLog(shader));
|
Log.e(TAG, "Could not compile shader: ${GLES20.glGetShaderInfoLog(shader)}")
|
||||||
GLES20.glDeleteShader(shader);
|
GLES20.glDeleteShader(shader)
|
||||||
return 0;
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
return shader;
|
return shader
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean draw(GL10 gl10, int width, int height) {
|
fun draw(gl10: GL10?, width: Int, height: Int): Boolean {
|
||||||
if (_textureId[0] == 0 || _program == 0) {
|
if (textureId[0] == 0 || program == 0) {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Расчет координат с уменьшением на 20%
|
// Расчет координат с уменьшением на 20%
|
||||||
float ratio = (float) width / height;
|
val ratio = width.toFloat() / height
|
||||||
float imageRatio = 1.0f; // Предполагаем квадратное изображение
|
val imageRatio = 1.0f // Предполагаем квадратное изображение
|
||||||
|
|
||||||
float scaleX, scaleY;
|
val (scaleX, scaleY) = if (ratio > imageRatio) {
|
||||||
if (ratio > imageRatio) {
|
|
||||||
// Шире, чем изображение
|
// Шире, чем изображение
|
||||||
scaleY = 0.8f; // Уменьшаем на 20%
|
val scaleY = 0.8f // Уменьшаем на 20%
|
||||||
scaleX = imageRatio / ratio * 0.8f;
|
val scaleX = imageRatio / ratio * 0.8f
|
||||||
|
scaleX to scaleY
|
||||||
} else {
|
} else {
|
||||||
// Уже, чем изображение
|
// Уже, чем изображение
|
||||||
scaleX = 0.8f; // Уменьшаем на 20%
|
val scaleX = 0.8f // Уменьшаем на 20%
|
||||||
scaleY = ratio / imageRatio * 0.8f;
|
val scaleY = ratio / imageRatio * 0.8f
|
||||||
|
scaleX to scaleY
|
||||||
}
|
}
|
||||||
|
|
||||||
// Координаты вершин и текстур
|
// Координаты вершин и текстур
|
||||||
float[] vertices = {
|
val vertices = floatArrayOf(
|
||||||
-scaleX, -scaleY, 0.0f, // нижний левый
|
-scaleX, -scaleY, 0.0f, // нижний левый
|
||||||
scaleX, -scaleY, 0.0f, // нижний правый
|
scaleX, -scaleY, 0.0f, // нижний правый
|
||||||
-scaleX, scaleY, 0.0f, // верхний левый
|
-scaleX, scaleY, 0.0f, // верхний левый
|
||||||
scaleX, scaleY, 0.0f // верхний правый
|
scaleX, scaleY, 0.0f // верхний правый
|
||||||
};
|
)
|
||||||
|
|
||||||
float[] texCoords = {
|
val texCoords = floatArrayOf(
|
||||||
0.0f, 1.0f, // нижний левый
|
0.0f, 1.0f, // нижний левый
|
||||||
1.0f, 1.0f, // нижний правый
|
1.0f, 1.0f, // нижний правый
|
||||||
0.0f, 0.0f, // верхний левый
|
0.0f, 0.0f, // верхний левый
|
||||||
1.0f, 0.0f // верхний правый
|
1.0f, 0.0f // верхний правый
|
||||||
};
|
)
|
||||||
|
|
||||||
// Создание буферов
|
// Создание буферов
|
||||||
ByteBuffer bb = ByteBuffer.allocateDirect(vertices.length * 4);
|
val vertexBuffer = ByteBuffer
|
||||||
bb.order(ByteOrder.nativeOrder());
|
.allocateDirect(vertices.size * 4)
|
||||||
FloatBuffer vertexBuffer = bb.asFloatBuffer();
|
.order(ByteOrder.nativeOrder())
|
||||||
vertexBuffer.put(vertices);
|
.asFloatBuffer()
|
||||||
vertexBuffer.position(0);
|
.apply {
|
||||||
|
put(vertices)
|
||||||
|
position(0)
|
||||||
|
}
|
||||||
|
|
||||||
bb = ByteBuffer.allocateDirect(texCoords.length * 4);
|
val texBuffer = ByteBuffer
|
||||||
bb.order(ByteOrder.nativeOrder());
|
.allocateDirect(texCoords.size * 4)
|
||||||
FloatBuffer texBuffer = bb.asFloatBuffer();
|
.order(ByteOrder.nativeOrder())
|
||||||
texBuffer.put(texCoords);
|
.asFloatBuffer()
|
||||||
texBuffer.position(0);
|
.apply {
|
||||||
|
put(texCoords)
|
||||||
|
position(0)
|
||||||
|
}
|
||||||
|
|
||||||
// Отрисовка с белым фоном
|
// Отрисовка с белым фоном
|
||||||
GLES20.glViewport(0, 0, width, height);
|
GLES20.glViewport(0, 0, width, height)
|
||||||
GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); // Белый цвет
|
GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f) // Белый цвет
|
||||||
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
|
||||||
|
|
||||||
GLES20.glUseProgram(_program);
|
GLES20.glUseProgram(program)
|
||||||
|
|
||||||
// Передача вершин
|
// Передача вершин
|
||||||
GLES20.glVertexAttribPointer(_attPosition, 3, GLES20.GL_FLOAT, false, 0, vertexBuffer);
|
GLES20.glVertexAttribPointer(attPosition, 3, GLES20.GL_FLOAT, false, 0, vertexBuffer)
|
||||||
GLES20.glEnableVertexAttribArray(_attPosition);
|
GLES20.glEnableVertexAttribArray(attPosition)
|
||||||
|
|
||||||
// Передача текстурных координат
|
// Передача текстурных координат
|
||||||
GLES20.glVertexAttribPointer(_attTexCoord, 2, GLES20.GL_FLOAT, false, 0, texBuffer);
|
GLES20.glVertexAttribPointer(attTexCoord, 2, GLES20.GL_FLOAT, false, 0, texBuffer)
|
||||||
GLES20.glEnableVertexAttribArray(_attTexCoord);
|
GLES20.glEnableVertexAttribArray(attTexCoord)
|
||||||
|
|
||||||
// Активация текстуры
|
// Активация текстуры
|
||||||
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
|
GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
|
||||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, _textureId[0]);
|
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId[0])
|
||||||
GLES20.glUniform1i(_attSampler, 0);
|
GLES20.glUniform1i(attSampler, 0)
|
||||||
|
|
||||||
// Отрисовка
|
// Отрисовка
|
||||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
|
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4)
|
||||||
|
|
||||||
// Отключение атрибутов
|
// Отключение атрибутов
|
||||||
GLES20.glDisableVertexAttribArray(_attPosition);
|
GLES20.glDisableVertexAttribArray(attPosition)
|
||||||
GLES20.glDisableVertexAttribArray(_attTexCoord);
|
GLES20.glDisableVertexAttribArray(attTexCoord)
|
||||||
|
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroy(GL10 gl10) {
|
fun destroy(gl10: GL10?) {
|
||||||
if (_textureId[0] != 0) {
|
if (textureId[0] != 0) {
|
||||||
GLES20.glDeleteTextures(1, _textureId, 0);
|
GLES20.glDeleteTextures(1, textureId, 0)
|
||||||
_textureId[0] = 0;
|
textureId[0] = 0
|
||||||
}
|
}
|
||||||
if (_program != 0) {
|
if (program != 0) {
|
||||||
GLES20.glDeleteProgram(_program);
|
GLES20.glDeleteProgram(program)
|
||||||
_program = 0;
|
program = 0
|
||||||
}
|
}
|
||||||
if (_vertexShader != 0) {
|
if (vertexShader != 0) {
|
||||||
GLES20.glDeleteShader(_vertexShader);
|
GLES20.glDeleteShader(vertexShader)
|
||||||
_vertexShader = 0;
|
vertexShader = 0
|
||||||
}
|
}
|
||||||
if (_fragmentShader != 0) {
|
if (fragmentShader != 0) {
|
||||||
GLES20.glDeleteShader(_fragmentShader);
|
GLES20.glDeleteShader(fragmentShader)
|
||||||
_fragmentShader = 0;
|
fragmentShader = 0
|
||||||
}
|
|
||||||
if (vBuffer != null) {
|
|
||||||
vBuffer.clear();
|
|
||||||
vBuffer = null;
|
|
||||||
}
|
}
|
||||||
|
vBuffer?.clear()
|
||||||
|
vBuffer = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.ea.ironmonkey.domain
|
||||||
|
|
||||||
|
enum class AssetLocationType {
|
||||||
|
EXTERNAL,
|
||||||
|
ASSETS,
|
||||||
|
OBB
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.ea.ironmonkey.domain
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
class FSNode {
|
||||||
|
@SerializedName("directory")
|
||||||
|
var directory: Boolean = false
|
||||||
|
|
||||||
|
@SerializedName("size")
|
||||||
|
var size: Int = 0
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user