Add Controller capability

This commit is contained in:
2023-12-13 22:24:01 +03:00
parent 5c8a58beac
commit d1de9be97f
14 changed files with 176 additions and 376 deletions
@@ -38,10 +38,10 @@ implements Parcelable {
final int mAction;
final int mKeyCode;
public KeyEvent(long l2, int n2, int n3, int n4) {
super(l2, n2);
this.mKeyCode = n3;
this.mAction = n4;
public KeyEvent(long eventTime, int controllerId, int keyCode, int action) {
super(eventTime, controllerId);
this.mKeyCode = keyCode;
this.mAction = action;
}
KeyEvent(Parcel parcel) {
@@ -26,16 +26,25 @@ implements Parcelable {
final SparseArray<Float> mAxis;
final SparseArray<Float> mPrecision;
public MotionEvent(long l2, int n2, float f2, float f3, float f4, float f5, float f6, float f7) {
super(l2, n2);
public MotionEvent(
long eventTime,
int controllerId,
float f2,
float f3,
float f4,
float f5,
float xPrecision,
float yPrecision
) {
super(eventTime, controllerId);
this.mAxis = new SparseArray(4);
this.mAxis.put(0, f2);
this.mAxis.put(1, f3);
this.mAxis.put(11, f4);
this.mAxis.put(14, f5);
this.mPrecision = new SparseArray(2);
this.mPrecision.put(0, f6);
this.mPrecision.put(1, f7);
this.mPrecision.put(0, xPrecision);
this.mPrecision.put(1, yPrecision);
}
public MotionEvent(long l2, int n2, int[] nArray, float[] fArray, int[] nArray2, float[] fArray2) {
@@ -43,8 +52,7 @@ implements Parcelable {
int n3 = nArray.length;
this.mAxis = new SparseArray(n3);
n2 = 0;
while (true) {
if (n2 >= n3) break;
while (n2 < n3) {
this.mAxis.put(nArray[n2], fArray[n2]);
++n2;
}
@@ -29,10 +29,10 @@ implements Parcelable {
final int mAction;
final int mState;
public StateEvent(long l2, int n2, int n3, int n4) {
super(l2, n2);
this.mState = n3;
this.mAction = n4;
public StateEvent(long eventTime, int controllerId, int state, int action) {
super(eventTime, controllerId);
this.mState = state;
this.mAction = action;
}
StateEvent(Parcel parcel) {
@@ -1,189 +0,0 @@
package com.ea.InAppWebBrowser;
import android.app.Activity;
import android.graphics.Paint;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.widget.RelativeLayout;
import com.ea.easp.VirtualKeyboardAndroidDelegate;
import java.util.UUID;
public class BrowserAndroid {
public static int gInstanceCount = 0;
public static Activity mActivity;
public static ViewGroup mViewGroup;
public final int JAVASCRIPT_DISABLE = 0;
public final int JAVASCRIPT_ENABLE = 1;
public final int SCROLLBARSTYLE_DEFAULT = 0;
public final int SCROLLBARSTYLE_INSIDE_INSET = 2;
public final int SCROLLBARSTYLE_INSIDE_OVERLAY = 1;
public final int SCROLLBARSTYLE_OUTSIDE_INSET = 4;
public final int SCROLLBARSTYLE_OUTSIDE_OVERLAY = 3;
public final int SCROLLBAR_INVISIBLE = 1;
public final int SCROLLBAR_VISIBLE = 0;
public int mInstanceID = 0;
public RelativeLayout mLayout;
public WebView mWebView;
public InAppWebBrowserWebViewClient mWebViewClient;
public static void Shutdown() {
ShutdownNativeImpl();
}
private static native void ShutdownNativeImpl();
public static void Startup(Activity activity, ViewGroup viewGroup) {
mActivity = activity;
mViewGroup = viewGroup;
StartupNativeImpl();
}
private static native void StartupNativeImpl();
public void EvaluateJavaScript(final String str) {
mActivity.runOnUiThread(new Runnable() {
/* class com.ea.InAppWebBrowser.BrowserAndroid.AnonymousClass5 */
public void run() {
UUID randomUUID;
do {
randomUUID = UUID.randomUUID();
} while (!BrowserAndroid.this.mWebViewClient.addUUID(randomUUID));
BrowserAndroid.this.mWebView.loadUrl("javascript: JavascriptCallback.ReceiveResult(eval(" + str + "), '" + randomUUID.toString() + "');");
}
});
}
public void LoadHTML(final String str) {
mActivity.runOnUiThread(new Runnable() {
/* class com.ea.InAppWebBrowser.BrowserAndroid.AnonymousClass3 */
public void run() {
BrowserAndroid.this.mWebView.loadData(str, "text/html", null);
}
});
}
public void OpenUrl(final String str) {
mActivity.runOnUiThread(new Runnable() {
/* class com.ea.InAppWebBrowser.BrowserAndroid.AnonymousClass2 */
public void run() {
BrowserAndroid.this.mWebView.loadUrl(str);
}
});
}
public void SetViewFrame(final int i, final int i2, final int i3, final int i4) {
if (this.mWebView != null) {
mActivity.runOnUiThread(new Runnable() {
/* class com.ea.InAppWebBrowser.BrowserAndroid.AnonymousClass4 */
public void run() {
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(i3, i4);
layoutParams.leftMargin = i;
layoutParams.topMargin = i2;
BrowserAndroid.this.mLayout.updateViewLayout(BrowserAndroid.this.mWebView, layoutParams);
}
});
}
}
public void destroy() {
mActivity.runOnUiThread(new Runnable() {
/* class com.ea.InAppWebBrowser.BrowserAndroid.AnonymousClass6 */
public void run() {
BrowserAndroid.this.mWebView.stopLoading();
BrowserAndroid.this.mWebView.setWebViewClient(null);
BrowserAndroid.this.mWebViewClient = null;
BrowserAndroid.this.mLayout.removeView(BrowserAndroid.this.mWebView);
BrowserAndroid.mViewGroup.removeView(BrowserAndroid.this.mLayout);
BrowserAndroid.this.mWebView = null;
BrowserAndroid.this.mLayout = null;
}
});
}
public void init(final int i, final int i2, final int i3, final int i4, final int i5, final int i6, final int i7, final boolean z, final boolean z2) {
int i8 = gInstanceCount;
gInstanceCount = i8 + 1;
this.mInstanceID = i8;
mActivity.runOnUiThread(new Runnable() {
/* class com.ea.InAppWebBrowser.BrowserAndroid.AnonymousClass1 */
public void run() {
boolean z = true;
BrowserAndroid.this.mLayout = new RelativeLayout(BrowserAndroid.mActivity);
BrowserAndroid.this.mWebView = new WebView(BrowserAndroid.mActivity);
BrowserAndroid.this.mWebViewClient = new InAppWebBrowserWebViewClient();
BrowserAndroid.this.mWebViewClient.mInstanceID = BrowserAndroid.this.mInstanceID;
BrowserAndroid.this.mWebView.setWebViewClient(BrowserAndroid.this.mWebViewClient);
if (z2) {
BrowserAndroid.this.mWebView.setBackgroundColor(0);
Class<?> cls = BrowserAndroid.this.mWebView.getClass();
try {
cls.getMethod("setLayerType", Integer.TYPE, Paint.class).invoke(BrowserAndroid.this.mWebView, Integer.valueOf(((Integer) cls.getField("LAYER_TYPE_SOFTWARE").get(BrowserAndroid.this.mWebView)).intValue()), null);
} catch (Exception e) {
}
}
if (!z) {
Class<?> cls2 = BrowserAndroid.this.mWebView.getClass();
try {
cls2.getMethod("setOverScrollMode", Integer.TYPE).invoke(BrowserAndroid.this.mWebView, Integer.valueOf(((Integer) cls2.getField("OVER_SCROLL_NEVER").get(BrowserAndroid.this.mWebView)).intValue()));
} catch (Exception e2) {
}
}
//BrowserAndroid.this.mWebView.requestFocus(TransportMediator.KEYCODE_MEDIA_RECORD);
BrowserAndroid.this.mWebView.setOnTouchListener(new View.OnTouchListener() {
/* class com.ea.InAppWebBrowser.BrowserAndroid.AnonymousClass1.AnonymousClass1 */
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case 0:
case 1:
if (view.hasFocus()) {
return false;
}
view.requestFocus();
return false;
default:
return false;
}
}
});
if (i5 == 1) {
BrowserAndroid.this.mWebView.getSettings().setJavaScriptEnabled(true);
}
BrowserAndroid.this.mWebView.setVerticalScrollBarEnabled(i6 == 0);
WebView webView = BrowserAndroid.this.mWebView;
if (i6 != 0) {
z = false;
}
webView.setHorizontalScrollBarEnabled(z);
BrowserAndroid.this.mWebView.addJavascriptInterface(new JavascriptInterface(BrowserAndroid.this.mWebViewClient), "JavascriptCallback");
switch (i7) {
case 1:
BrowserAndroid.this.mWebView.setScrollBarStyle(0);
break;
case 2:
BrowserAndroid.this.mWebView.setScrollBarStyle(16777216);
break;
case 3:
BrowserAndroid.this.mWebView.setScrollBarStyle(VirtualKeyboardAndroidDelegate.IME_FLAG_NO_FULLSCREEN);
break;
case 4:
BrowserAndroid.this.mWebView.setScrollBarStyle(50331648);
break;
}
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(i3, i4);
layoutParams.leftMargin = i;
layoutParams.topMargin = i2;
BrowserAndroid.this.mLayout.addView(BrowserAndroid.this.mWebView, layoutParams);
BrowserAndroid.mViewGroup.addView(BrowserAndroid.this.mLayout);
}
});
}
}
@@ -1,68 +0,0 @@
package com.ea.InAppWebBrowser;
import android.graphics.Bitmap;
import android.util.Log;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.UUID;
public class InAppWebBrowserWebViewClient extends WebViewClient {
public int mInstanceID = 0;
private SortedSet<UUID> mJavascriptIds = Collections.synchronizedSortedSet(new TreeSet());
public native void OnJavascriptResult(String str, int i);
public native void OnLoadError(String str, int i);
public native void OnLoadFinished(String str, int i);
public native void OnLoadStarted(String str, int i);
public native boolean ShouldLoadURL(String str, int i);
public boolean addUUID(UUID uuid) {
if (this.mJavascriptIds.contains(uuid)) {
return false;
}
this.mJavascriptIds.add(uuid);
return true;
}
public void onJavascriptResult(String str, String str2) throws Exception {
UUID fromString = UUID.fromString(str2);
if (fromString == null || !this.mJavascriptIds.contains(fromString)) {
throw new Exception("Unable to verify validity of script result.");
}
this.mJavascriptIds.remove(fromString);
OnJavascriptResult(str, this.mInstanceID);
}
public void onLoadResource(WebView webView, String str) {
Log.e("InAppWebBrowserWebViewClient", "onLoadResource: " + str);
OnLoadStarted(str, this.mInstanceID);
}
public void onPageFinished(WebView webView, String str) {
Log.e("InAppWebBrowserWebViewClient", "onPageFinished: " + str);
OnLoadFinished(str, this.mInstanceID);
}
public void onPageStarted(WebView webView, String str, Bitmap bitmap) {
Log.e("InAppWebBrowserWebViewClient", "onPageStarted: " + str);
OnLoadStarted(str, this.mInstanceID);
}
public void onReceivedError(WebView webView, int i, String str, String str2) {
Log.e("InAppWebBrowserWebViewClient", "onReceivedError");
OnLoadError("Loading Error. URL: " + str2 + " ErrorCode: " + i + " Description: " + str, this.mInstanceID);
}
@Override // android.webkit.WebViewClient
public boolean shouldOverrideUrlLoading(WebView webView, String str) {
Log.e("InAppWebBrowserWebViewClient", "shouldOverrideUrlLoading: " + str);
return !ShouldLoadURL(str, this.mInstanceID);
}
}
@@ -1,13 +0,0 @@
package com.ea.InAppWebBrowser;
public class JavascriptInterface {
private InAppWebBrowserWebViewClient mWebViewClient;
public JavascriptInterface(InAppWebBrowserWebViewClient inAppWebBrowserWebViewClient) {
this.mWebViewClient = inAppWebBrowserWebViewClient;
}
public void ReceiveResult(String str, String str2) throws Exception {
this.mWebViewClient.onJavascriptResult(str, str2);
}
}
@@ -1,72 +0,0 @@
package com.ea.InputMan;
import androidx.fragment.app.FragmentTransaction;
import androidx.core.view.MotionEventCompat;
import android.view.MotionEvent;
public class InputMan {
private boolean gbMotionEvent_GetSource = false;
public InputMan() {
try {
MotionEvent.class.getMethod("getSource");
this.gbMotionEvent_GetSource = true;
} catch (Exception e) {
}
}
private static int GetEventType(int i) {
switch (i) {
case 0:
case 4:
case 5:
default:
return 0;
case 1:
case 6:
return 2;
case 2:
return 1;
case 3:
return 3;
}
}
private int GetSource(MotionEvent motionEvent) {
if (!this.gbMotionEvent_GetSource) {
return 0;
}
int source = motionEvent.getSource();
if ((source & 2) == 0) {
return -1;
}
switch (source) {
case 1048584:
return 10;
case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:
break;
}
return 0;
}
private static native boolean InputMan_OnMotionEvent(int i, int i2, float f, float f2, int i3, float f3);
public boolean onTouchEvent(int i, MotionEvent motionEvent) {
boolean z = false;
int historySize = motionEvent.getHistorySize();
int pointerCount = motionEvent.getPointerCount();
int GetSource = GetSource(motionEvent);
int GetEventType = GetEventType(motionEvent.getAction() & MotionEventCompat.ACTION_MASK);
if (GetSource >= 0) {
for (int i2 = 0; i2 < historySize; i2++) {
for (int i3 = 0; i3 < pointerCount; i3++) {
InputMan_OnMotionEvent(i, motionEvent.getPointerId(i3), motionEvent.getHistoricalX(i3, i2), motionEvent.getHistoricalY(i3, i2), GetEventType, motionEvent.getHistoricalPressure(i3, i2));
}
}
for (int i4 = 0; i4 < pointerCount; i4++) {
z = InputMan_OnMotionEvent(i, motionEvent.getPointerId(i4), motionEvent.getX(i4), motionEvent.getY(i4), GetEventType, motionEvent.getPressure(i4));
}
}
return z;
}
}
@@ -46,11 +46,6 @@ class EASPHandler(
initJNI()
DeviceInfoUtil.init()
PackageUtil.init()
//this.mFacebookAgent = new FacebookAgentJNI(mActivity, this.mTaskLauncher);
//this.mFacebookAgent.init();
//BrowserAndroid.Startup(mActivity, mViewGroup);
//this.mAndroidMarketJNI = new MarketJNI(mActivity, this.mTaskLauncher, MarketJNI.StoreType.UNKNOWN);
//this.mAndroidMarketJNI.init();
mPhysicalKeyboard = PhysicalKeyboardAndroid(mTaskLauncher)
Debug.Log.d(this.javaClass.name, "...onCreate()")
}
@@ -58,9 +53,6 @@ class EASPHandler(
fun onDestroy() {
Debug.Log.d(this.javaClass.name, "onDestroy()...")
mPhysicalKeyboard = null
//this.mAndroidMarketJNI.shutdown();
//BrowserAndroid.Shutdown();
//this.mFacebookAgent.shutdown();
PackageUtil.shutdown()
DeviceInfoUtil.shutdown()
shutdownJNI()
@@ -19,6 +19,7 @@ import androidx.compose.ui.platform.ComposeView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import com.bda.controller.StateEvent
class ComposeFrameLayout @JvmOverloads constructor(
@@ -32,13 +33,16 @@ class ComposeFrameLayout @JvmOverloads constructor(
addView(
ComposeView(context).apply {
setContent {
Box{
/*Box{
Button(onClick = {
Toast.makeText(context, "Hello!", Toast.LENGTH_LONG).show()
var event = StateEvent(System.currentTimeMillis(), 1, StateEvent.STATE_CONNECTION, StateEvent.ACTION_CONNECTED)
//MogaController.nativeOnStateEvent(event)
}) {
Text("ComposeButton")
}
}
}*/
}
}
)
@@ -1,11 +1,13 @@
package com.ea.ironmonkey
import android.annotation.SuppressLint
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
@@ -15,17 +17,26 @@ 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.devmenu.util.SaveFileObserver
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
@@ -60,8 +71,6 @@ class GameActivity : ComponentActivity(), DrawFrameListener {
private val saveFile = "/data/data/${BuildConfig.APPLICATION_ID}/files/var/nfstr_save.sb"
private val dest = "/sdcard/Android/data/${BuildConfig.APPLICATION_ID}/files/saveTracking"
private val saveFileObserver = com.megboyzz.devmenu.util.SaveFileObserver(saveFile, dest)
companion object {
const val STATE_RESTORE_CONTEXT = 7
@@ -120,6 +129,7 @@ class GameActivity : ComponentActivity(), DrawFrameListener {
Log.w(this.localClassName, "Missing WAKE_LOCK permission.")
}
}
this.assets
}
private fun wakeLockRelease() {
@@ -295,10 +305,24 @@ class GameActivity : ComponentActivity(), DrawFrameListener {
public override fun onCreate(bundle: Bundle?) {
saveFileObserver.startWatching()
super.onCreate(bundle)
registerControllerHandlers(
onInputDeviceAdded = {
Log.i("Controller", "controller is connected!")
val event = createConnectEvent(it)
MogaController.nativeOnStateEvent(event)
},
onInputDeviceRemoved = {
Log.i("Controller", "controller is disconnected!")
val event = createDisconnectEvent(it)
MogaController.nativeOnStateEvent(event)
},
onInputDeviceChanged = {
Log.i("Controller", "controller is changed!")
}
)
val className = this.localClassName
val currentState = lifecycle.currentState
@@ -347,13 +371,12 @@ class GameActivity : ComponentActivity(), DrawFrameListener {
ApplicationLifecycle.onActivityCreate(bundle, this)
Log.d(className, "nativeOnCreate")
nativeOnCreate()
}
public override fun onDestroy() {
saveFileObserver.stopWatching()
Log.i("Debug", "onDestroy")
Log.i(this.localClassName, "onDestroy")
super.onDestroy()
@@ -367,7 +390,27 @@ class GameActivity : ComponentActivity(), DrawFrameListener {
exitProcess(0)
}
override fun dispatchGenericMotionEvent(ev: MotionEvent?): Boolean {
Log.i("GameActivity", ev.toString())
MogaController.onMotionEvent(androidMotionEventToBda(ev!!))
return super.dispatchGenericMotionEvent(ev)
}
@SuppressLint("RestrictedApi")
override fun dispatchKeyEvent(event: KeyEvent?): Boolean {
MogaController.onKeyEvent(androidKeyEventToBda(event!!))
return super.dispatchKeyEvent(event)
}
override fun onDrawFrame(gl10: GL10) {
//InputDevice.getDeviceIds()
//InputDevice.getDevice(0).sources
Log.d(this.localClassName, "onDrawFrame state=$state")
var isStarted = false
when (state) {
@@ -479,6 +522,10 @@ class GameActivity : ComponentActivity(), DrawFrameListener {
gameGLSurfaceView.onResume()
ApplicationLifecycle.onActivityResume(this)
nativeOnResume()
actionForAllControllers {
val event = createConnectEvent(it)
MogaController.nativeOnStateEvent(event)
}
}
public override fun onSaveInstanceState(bundle: Bundle) {
+91
View File
@@ -0,0 +1,91 @@
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
)