Добавил mpcore
This commit is contained in:
Generated
+1
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
|
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||||
<component name="GradleSettings">
|
<component name="GradleSettings">
|
||||||
<option name="linkedExternalProjectsSettings">
|
<option name="linkedExternalProjectsSettings">
|
||||||
<GradleProjectSettings>
|
<GradleProjectSettings>
|
||||||
|
|||||||
Generated
-1
@@ -1,4 +1,3 @@
|
|||||||
<?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" default="true" 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">
|
||||||
|
|||||||
@@ -51,5 +51,6 @@ dependencies {
|
|||||||
implementation("com.parse.bolts:bolts-tasks:1.4.0")
|
implementation("com.parse.bolts:bolts-tasks:1.4.0")
|
||||||
implementation("org.apache.httpcomponents:httpclient-android:4.3.5.1")
|
implementation("org.apache.httpcomponents:httpclient-android:4.3.5.1")
|
||||||
implementation("com.google.code.gson:gson:2.7")
|
implementation("com.google.code.gson:gson:2.7")
|
||||||
|
implementation(project(":mpcore"))
|
||||||
//implementation("com.android.support:support-v4:28.0.0")
|
//implementation("com.android.support:support-v4:28.0.0")
|
||||||
}
|
}
|
||||||
@@ -1,180 +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 java.util.UUID;
|
|
||||||
|
|
||||||
/* loaded from: classes.dex */
|
|
||||||
public class BrowserAndroid {
|
|
||||||
public static int gInstanceCount;
|
|
||||||
public static Activity mActivity;
|
|
||||||
public static ViewGroup mViewGroup;
|
|
||||||
public RelativeLayout mLayout;
|
|
||||||
public WebView mWebView;
|
|
||||||
public InAppWebBrowserWebViewClient mWebViewClient;
|
|
||||||
public final int SCROLLBAR_VISIBLE = 0;
|
|
||||||
public final int SCROLLBAR_INVISIBLE = 1;
|
|
||||||
public final int JAVASCRIPT_DISABLE = 0;
|
|
||||||
public final int JAVASCRIPT_ENABLE = 1;
|
|
||||||
public final int SCROLLBARSTYLE_DEFAULT = 0;
|
|
||||||
public final int SCROLLBARSTYLE_INSIDE_OVERLAY = 1;
|
|
||||||
public final int SCROLLBARSTYLE_INSIDE_INSET = 2;
|
|
||||||
public final int SCROLLBARSTYLE_OUTSIDE_OVERLAY = 3;
|
|
||||||
public final int SCROLLBARSTYLE_OUTSIDE_INSET = 4;
|
|
||||||
public int mInstanceID = 0;
|
|
||||||
|
|
||||||
private static native void ShutdownNativeImpl();
|
|
||||||
|
|
||||||
private static native void StartupNativeImpl();
|
|
||||||
|
|
||||||
public void BrowserAndroid() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Startup(Activity activity, ViewGroup viewGroup) {
|
|
||||||
mActivity = activity;
|
|
||||||
mViewGroup = viewGroup;
|
|
||||||
StartupNativeImpl();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Shutdown() {
|
|
||||||
ShutdownNativeImpl();
|
|
||||||
}
|
|
||||||
|
|
||||||
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() { // from class: com.ea.InAppWebBrowser.BrowserAndroid.1
|
|
||||||
@Override // java.lang.Runnable
|
|
||||||
public void run() {
|
|
||||||
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 unused) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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 unused2) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BrowserAndroid.this.mWebView.requestFocus(130);
|
|
||||||
BrowserAndroid.this.mWebView.setOnTouchListener(new View.OnTouchListener() { // from class: com.ea.InAppWebBrowser.BrowserAndroid.1.1
|
|
||||||
@Override // android.view.View.OnTouchListener
|
|
||||||
public boolean onTouch(View view, MotionEvent motionEvent) {
|
|
||||||
switch (motionEvent.getAction()) {
|
|
||||||
case 0:
|
|
||||||
case 1:
|
|
||||||
if (!view.hasFocus()) {
|
|
||||||
view.requestFocus();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (i5 == 1) {
|
|
||||||
BrowserAndroid.this.mWebView.getSettings().setJavaScriptEnabled(true);
|
|
||||||
}
|
|
||||||
BrowserAndroid.this.mWebView.setVerticalScrollBarEnabled(i6 == 0);
|
|
||||||
BrowserAndroid.this.mWebView.setHorizontalScrollBarEnabled(i6 == 0);
|
|
||||||
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(33554432);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OpenUrl(final String str) {
|
|
||||||
mActivity.runOnUiThread(new Runnable() { // from class: com.ea.InAppWebBrowser.BrowserAndroid.2
|
|
||||||
@Override // java.lang.Runnable
|
|
||||||
public void run() {
|
|
||||||
BrowserAndroid.this.mWebView.loadUrl(str);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LoadHTML(final String str) {
|
|
||||||
mActivity.runOnUiThread(new Runnable() { // from class: com.ea.InAppWebBrowser.BrowserAndroid.3
|
|
||||||
@Override // java.lang.Runnable
|
|
||||||
public void run() {
|
|
||||||
BrowserAndroid.this.mWebView.loadData(str, "text/html", null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetViewFrame(final int i, final int i2, final int i3, final int i4) {
|
|
||||||
if (this.mWebView == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mActivity.runOnUiThread(new Runnable() { // from class: com.ea.InAppWebBrowser.BrowserAndroid.4
|
|
||||||
@Override // java.lang.Runnable
|
|
||||||
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 EvaluateJavaScript(final String str) {
|
|
||||||
mActivity.runOnUiThread(new Runnable() { // from class: com.ea.InAppWebBrowser.BrowserAndroid.5
|
|
||||||
@Override // java.lang.Runnable
|
|
||||||
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 destroy() {
|
|
||||||
mActivity.runOnUiThread(new Runnable() { // from class: com.ea.InAppWebBrowser.BrowserAndroid.6
|
|
||||||
@Override // java.lang.Runnable
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,74 +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;
|
|
||||||
|
|
||||||
/* loaded from: classes.dex */
|
|
||||||
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);
|
|
||||||
|
|
||||||
@Override // android.webkit.WebViewClient
|
|
||||||
public boolean shouldOverrideUrlLoading(WebView webView, String str) {
|
|
||||||
Log.e("InAppWebBrowserWebViewClient", "shouldOverrideUrlLoading: " + str);
|
|
||||||
return !ShouldLoadURL(str, this.mInstanceID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // android.webkit.WebViewClient
|
|
||||||
public void onLoadResource(WebView webView, String str) {
|
|
||||||
Log.e("InAppWebBrowserWebViewClient", "onLoadResource: " + str);
|
|
||||||
OnLoadStarted(str, this.mInstanceID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // android.webkit.WebViewClient
|
|
||||||
public void onPageStarted(WebView webView, String str, Bitmap bitmap) {
|
|
||||||
Log.e("InAppWebBrowserWebViewClient", "onPageStarted: " + str);
|
|
||||||
OnLoadStarted(str, this.mInstanceID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // android.webkit.WebViewClient
|
|
||||||
public void onPageFinished(WebView webView, String str) {
|
|
||||||
Log.e("InAppWebBrowserWebViewClient", "onPageFinished: " + str);
|
|
||||||
OnLoadFinished(str, this.mInstanceID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // android.webkit.WebViewClient
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onJavascriptResult(String str, String str2) throws Exception {
|
|
||||||
UUID fromString = UUID.fromString(str2);
|
|
||||||
if (fromString != null && this.mJavascriptIds.contains(fromString)) {
|
|
||||||
this.mJavascriptIds.remove(fromString);
|
|
||||||
OnJavascriptResult(str, this.mInstanceID);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw new Exception("Unable to verify validity of script result.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean addUUID(UUID uuid) {
|
|
||||||
if (this.mJavascriptIds.contains(uuid)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this.mJavascriptIds.add(uuid);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package com.ea.InAppWebBrowser;
|
|
||||||
|
|
||||||
/* loaded from: classes.dex */
|
|
||||||
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,56 +0,0 @@
|
|||||||
package com.ea.InputMan;
|
|
||||||
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
|
|
||||||
/* loaded from: classes.dex */
|
|
||||||
public class InputMan {
|
|
||||||
private boolean gbMotionEvent_GetSource;
|
|
||||||
|
|
||||||
private static int GetEventType(int i) {
|
|
||||||
switch (i) {
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static native boolean InputMan_OnMotionEvent(int i, int i2, float f, float f2, int i3, float f3);
|
|
||||||
|
|
||||||
public InputMan() {
|
|
||||||
this.gbMotionEvent_GetSource = false;
|
|
||||||
try {
|
|
||||||
MotionEvent.class.getMethod("getSource", new Class[0]);
|
|
||||||
this.gbMotionEvent_GetSource = true;
|
|
||||||
} catch (Exception unused) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int GetSource(MotionEvent motionEvent) {
|
|
||||||
if (!this.gbMotionEvent_GetSource) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int source = motionEvent.getSource();
|
|
||||||
if ((source & 2) != 0) {
|
|
||||||
return (source == 8194 || source != 1048584) ? 0 : 10;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onTouchEvent(int i, MotionEvent motionEvent) {
|
|
||||||
int historySize = motionEvent.getHistorySize();
|
|
||||||
int pointerCount = motionEvent.getPointerCount();
|
|
||||||
int GetSource = GetSource(motionEvent);
|
|
||||||
int GetEventType = GetEventType(motionEvent.getAction() & 255);
|
|
||||||
if (GetSource < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
boolean z = false;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -68,6 +68,8 @@ import javax.microedition.khronos.egl.EGLConfig;
|
|||||||
import javax.microedition.khronos.opengles.GL10;
|
import javax.microedition.khronos.opengles.GL10;
|
||||||
import org.fmod.FMODAudioDevice;
|
import org.fmod.FMODAudioDevice;
|
||||||
|
|
||||||
|
import nfs.mod.mpcore.MultiplayerCore;
|
||||||
|
|
||||||
/* loaded from: classes.dex */
|
/* loaded from: classes.dex */
|
||||||
public class GameActivityMain extends AppCompatActivity implements DrawFrameListener {
|
public class GameActivityMain extends AppCompatActivity implements DrawFrameListener {
|
||||||
private static final String DOWNLOAD_PROPERTIES = "downloadcontent/config.properties";
|
private static final String DOWNLOAD_PROPERTIES = "downloadcontent/config.properties";
|
||||||
@@ -310,6 +312,7 @@ public class GameActivityMain extends AppCompatActivity implements DrawFrameList
|
|||||||
System.loadLibrary("c++_shared");
|
System.loadLibrary("c++_shared");
|
||||||
System.loadLibrary(Global.NIMBLE_ID);
|
System.loadLibrary(Global.NIMBLE_ID);
|
||||||
System.loadLibrary("app");
|
System.loadLibrary("app");
|
||||||
|
MultiplayerCore.INSTANCE.loadCore();
|
||||||
Log.d(TAG, "Init EAIO/EAMIO");
|
Log.d(TAG, "Init EAIO/EAMIO");
|
||||||
EAIO.Startup(this);
|
EAIO.Startup(this);
|
||||||
StorageDirectory.Startup(this);
|
StorageDirectory.Startup(this);
|
||||||
|
|||||||
@@ -1,112 +1,167 @@
|
|||||||
package com.ea.ironmonkey;
|
package com.ea.ironmonkey;
|
||||||
|
|
||||||
import static android.os.Build.VERSION_CODES.R;
|
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.provider.Settings;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.core.app.ActivityCompat;
|
import androidx.core.app.ActivityCompat;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
|
|
||||||
/* loaded from: classes.dex */
|
import java.util.ArrayList;
|
||||||
public class PermissionsActivity extends Activity {
|
import java.util.List;
|
||||||
private static final int PERMISSIONS_REQUEST = 3;
|
|
||||||
|
|
||||||
protected void onCreate() {
|
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();
|
checkPermissions();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkPermissions() {
|
private void checkPermissions() {
|
||||||
if (ContextCompat.checkSelfPermission(this, "android.permission.WRITE_EXTERNAL_STORAGE") != 0) {
|
if (areAllPermissionsGranted()) {
|
||||||
if (ActivityCompat.shouldShowRequestPermissionRationale(this, "android.permission.WRITE_EXTERNAL_STORAGE")) {
|
|
||||||
showPermissionDialog(false);
|
|
||||||
} else {
|
|
||||||
requestSecurityPermissions();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
initActivity();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // android.app.Activity
|
|
||||||
public void onRequestPermissionsResult(int i, String[] strArr, int[] iArr) {
|
|
||||||
if (i != 3) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (iArr.length == 1 && iArr[0] == 0) {
|
|
||||||
initActivity();
|
initActivity();
|
||||||
} else {
|
} else {
|
||||||
showPermissionDialog(true);
|
requestPermissions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showPermissionDialog(final boolean z) {
|
private boolean areAllPermissionsGranted() {
|
||||||
Context context = this;
|
for (String permission : getRequiredPermissions()) {
|
||||||
runOnUiThread(new Runnable() { // from class: com.ea.ironmonkey.PermissionsActivity.1
|
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
|
||||||
@Override // java.lang.Runnable
|
return false;
|
||||||
public void run() {
|
}
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
|
||||||
builder.setMessage("DIALOG");
|
|
||||||
builder.setPositiveButton("OK", (dialogInterface, i) -> {
|
|
||||||
if (!z) {
|
|
||||||
PermissionsActivity.this.requestSecurityPermissions();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
Intent intent = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS", Uri.parse("package:" + PermissionsActivity.this.getPackageName()));
|
|
||||||
|
|
||||||
PermissionsActivity.this.startActivityForResult(intent, 3);
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.setNegativeButton("DIALOG", (dialogInterface, i) -> PermissionsActivity.this.finish());
|
|
||||||
AlertDialog create = builder.create();
|
|
||||||
create.setCanceledOnTouchOutside(false);
|
|
||||||
create.setOnKeyListener((dialogInterface, i, keyEvent) -> {
|
|
||||||
if (i != 4) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
PermissionsActivity.this.finish();
|
|
||||||
dialogInterface.dismiss();
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
create.show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void requestSecurityPermissions() {
|
private String[] getRequiredPermissions() {
|
||||||
ActivityCompat.requestPermissions(this, new String[]{"android.permission.WRITE_EXTERNAL_STORAGE"}, 3);
|
List<String> permissions = new ArrayList<>();
|
||||||
ActivityCompat.requestPermissions(this, new String[]{"android.permission.READ_EXTERNAL_STORAGE"}, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // android.app.Activity
|
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
|
||||||
public void onCreate(Bundle bundle) {
|
// Android 9 (Pie) и ниже - нужны оба разрешения
|
||||||
super.onCreate(bundle);
|
permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||||
onCreate();
|
permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||||
|
} else if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
|
||||||
|
// Android 10 (Q) - только чтение
|
||||||
|
permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||||
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
// Android 13+ - новые раздельные разрешения для медиа
|
||||||
|
permissions.add(Manifest.permission.READ_MEDIA_IMAGES);
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override // android.app.Activity
|
|
||||||
public void onActivityResult(int i, int i2, Intent intent) {
|
|
||||||
super.onActivityResult(i, i2, intent);
|
|
||||||
if (i == 3) {
|
|
||||||
if (ContextCompat.checkSelfPermission(this, "android.permission.WRITE_EXTERNAL_STORAGE") == 0
|
|
||||||
&& ContextCompat.checkSelfPermission(this, "android.permission.READ_EXTERNAL_STORAGE") == 0
|
|
||||||
) {
|
|
||||||
initActivity();
|
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
showPermissionDialog(true);
|
// Android 11-12 - только чтение
|
||||||
return;
|
permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return permissions.toArray(new String[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void requestPermissions() {
|
||||||
|
String[] permissions = getRequiredPermissions();
|
||||||
|
boolean shouldShowRationale = false;
|
||||||
|
|
||||||
|
for (String permission : permissions) {
|
||||||
|
if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) {
|
||||||
|
shouldShowRationale = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shouldShowRationale) {
|
||||||
|
showPermissionRationaleDialog();
|
||||||
|
} else {
|
||||||
|
requestPermissionLauncher.launch(permissions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showPermissionRationaleDialog() {
|
||||||
|
new AlertDialog.Builder(this)
|
||||||
|
.setTitle("Требуются разрешения")
|
||||||
|
.setMessage("Для корректной работы приложения необходимы разрешения на доступ к хранилищу. Пожалуйста, предоставьте запрашиваемые разрешения.")
|
||||||
|
.setPositiveButton("OK", (dialog, which) -> requestPermissionLauncher.launch(getRequiredPermissions()))
|
||||||
|
.setNegativeButton("Отмена", (dialog, which) -> finish())
|
||||||
|
.setCancelable(false)
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showPermanentlyDeniedDialog() {
|
||||||
|
new AlertDialog.Builder(this)
|
||||||
|
.setTitle("Разрешения отклонены")
|
||||||
|
.setMessage("Вы навсегда отклонили некоторые разрешения. Чтобы использовать приложение, предоставьте разрешения в настройках.")
|
||||||
|
.setPositiveButton("Настройки", (dialog, which) -> openAppSettings())
|
||||||
|
.setNegativeButton("Выход", (dialog, which) -> finish())
|
||||||
|
.setCancelable(false)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openAppSettings() {
|
||||||
|
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||||
|
intent.setData(Uri.parse("package:" + getPackageName()));
|
||||||
|
startActivityForResult(intent, SETTINGS_REQUEST_CODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
if (requestCode == SETTINGS_REQUEST_CODE) {
|
||||||
|
if (areAllPermissionsGranted()) {
|
||||||
initActivity();
|
initActivity();
|
||||||
|
} else {
|
||||||
|
handlePermissionsDenied();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initActivity() {
|
private void initActivity() {
|
||||||
@@ -115,6 +170,8 @@ public class PermissionsActivity extends Activity {
|
|||||||
finish();
|
finish();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
Toast.makeText(this, "Ошибка запуска приложения", Toast.LENGTH_SHORT).show();
|
||||||
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,15 +9,6 @@ public class NimbleApplicationConfiguration {
|
|||||||
private static final String LOG_TITLE = "AppConfig";
|
private static final String LOG_TITLE = "AppConfig";
|
||||||
|
|
||||||
public static boolean configValueExists(String str) {
|
public static boolean configValueExists(String str) {
|
||||||
Log.Helper.LOGPUBLICFUNCS(LOG_TITLE);
|
|
||||||
try {
|
|
||||||
Activity currentActivity = ApplicationEnvironment.getCurrentActivity();
|
|
||||||
if (currentActivity != null) {
|
|
||||||
return currentActivity.getPackageManager().getApplicationInfo(currentActivity.getPackageName(), 128).metaData.containsKey(str);
|
|
||||||
}
|
|
||||||
} catch (PackageManager.NameNotFoundException unused) {
|
|
||||||
Log.Helper.LOGES(LOG_TITLE, "Config value of key '%s' cannot be retrieved.", str);
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,14 +19,6 @@ public class NimbleApplicationConfiguration {
|
|||||||
|
|
||||||
public static String getConfigValueAsString(String str, String str2) {
|
public static String getConfigValueAsString(String str, String str2) {
|
||||||
Log.Helper.LOGPUBLICFUNCS(LOG_TITLE);
|
Log.Helper.LOGPUBLICFUNCS(LOG_TITLE);
|
||||||
try {
|
|
||||||
Activity currentActivity = ApplicationEnvironment.getCurrentActivity();
|
|
||||||
if (currentActivity != null) {
|
|
||||||
return currentActivity.getPackageManager().getApplicationInfo(currentActivity.getPackageName(), 128).metaData.getString(str);
|
|
||||||
}
|
|
||||||
} catch (PackageManager.NameNotFoundException unused) {
|
|
||||||
Log.Helper.LOGES(LOG_TITLE, "Config value of key '%s' cannot be retrieved.", str);
|
|
||||||
}
|
|
||||||
return str2;
|
return str2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,45 +25,7 @@ public class NimbleLocalNotificationsImpl extends Component implements INimbleLo
|
|||||||
|
|
||||||
@Override // com.ea.nimble.Component
|
@Override // com.ea.nimble.Component
|
||||||
public void restore() {
|
public void restore() {
|
||||||
Log.Helper.LOGPUBLICFUNC(this);
|
|
||||||
Persistence persistenceForNimbleComponent = PersistenceService.getPersistenceForNimbleComponent(getComponentId(), Persistence.Storage.CACHE);
|
|
||||||
if (persistenceForNimbleComponent.hasKey(PERSISTENCE_ENABLED_KEY)) {
|
|
||||||
this.m_enabled = persistenceForNimbleComponent.getBoolValue(PERSISTENCE_ENABLED_KEY);
|
|
||||||
} else {
|
|
||||||
this.m_enabled = true;
|
|
||||||
}
|
|
||||||
if (persistenceForNimbleComponent.hasKey(PERSISTENCE_NOTIFICATION_MAP_KEY)) {
|
|
||||||
this.m_notificationMap = (HashMap) persistenceForNimbleComponent.getValue(PERSISTENCE_NOTIFICATION_MAP_KEY);
|
|
||||||
Iterator<String> it = this.m_notificationMap.keySet().iterator();
|
|
||||||
Date date = new Date();
|
|
||||||
while (it.hasNext()) {
|
|
||||||
String next = it.next();
|
|
||||||
Date date2 = this.m_notificationMap.get(next);
|
|
||||||
if (date2.before(date)) {
|
|
||||||
Log.Helper.LOGV(this, "restore(): Removed notification list key " + next + " from map because its date (" + date2.toString() + ") is in the past!", new Object[0]);
|
|
||||||
it.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
saveNotificationMap();
|
|
||||||
} else {
|
|
||||||
this.m_notificationMap = new HashMap<>();
|
|
||||||
}
|
|
||||||
Context applicationContext = ApplicationEnvironment.getComponent().getApplicationContext();
|
|
||||||
if (Build.VERSION.SDK_INT >= 26) {
|
|
||||||
NotificationManager notificationManager = (NotificationManager) applicationContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
|
||||||
if (NimbleApplicationConfiguration.configValueExists(Global.NOTIFICATION_CHANNEL_LOCAL_NOTIFICATION_ID_KEY)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String str = Global.NOTIFICATION_CHANNEL_DEFAULT_NAME_VALUE;
|
|
||||||
if (NimbleApplicationConfiguration.configValueExists(Global.NOTIFICATION_CHANNEL_DEFAULT_NAME_KEY)) {
|
|
||||||
str = applicationContext.getResources().getString(NimbleApplicationConfiguration.getConfigValueAsInt(Global.NOTIFICATION_CHANNEL_DEFAULT_NAME_KEY));
|
|
||||||
}
|
|
||||||
NotificationChannel notificationChannel = new NotificationChannel(Global.NOTIFICATION_CHANNEL_DEFAULT_ID, str, NotificationManager.IMPORTANCE_DEFAULT);
|
|
||||||
if (NimbleApplicationConfiguration.configValueExists(Global.NOTIFICATION_CHANNEL_DEFAULT_DESCRIPTION_KEY)) {
|
|
||||||
notificationChannel.setDescription(applicationContext.getResources().getString(NimbleApplicationConfiguration.getConfigValueAsInt(Global.NOTIFICATION_CHANNEL_DEFAULT_DESCRIPTION_KEY)));
|
|
||||||
}
|
|
||||||
notificationManager.createNotificationChannel(notificationChannel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override // com.ea.nimble.INimbleLocalNotifications
|
@Override // com.ea.nimble.INimbleLocalNotifications
|
||||||
|
|||||||
Binary file not shown.
@@ -2,4 +2,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.android.application) apply false
|
alias(libs.plugins.android.application) apply false
|
||||||
alias(libs.plugins.kotlin.android) apply false
|
alias(libs.plugins.kotlin.android) apply false
|
||||||
|
alias(libs.plugins.android.library) apply false
|
||||||
}
|
}
|
||||||
@@ -18,5 +18,6 @@ material = { group = "com.google.android.material", name = "material", version.r
|
|||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
android-library = { id = "com.android.library", version.ref = "agp" }
|
||||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.android.library)
|
||||||
|
alias(libs.plugins.kotlin.android)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "nfs.mod.mpcore"
|
||||||
|
compileSdk = 35
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk = 27
|
||||||
|
|
||||||
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
consumerProguardFiles("consumer-rules.pro")
|
||||||
|
externalNativeBuild {
|
||||||
|
cmake {
|
||||||
|
cppFlags("-std=c++17")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
isMinifyEnabled = false
|
||||||
|
proguardFiles(
|
||||||
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
|
"proguard-rules.pro"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
externalNativeBuild {
|
||||||
|
cmake {
|
||||||
|
path("src/main/cpp/CMakeLists.txt")
|
||||||
|
version = "3.22.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "11"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
implementation(libs.androidx.core.ktx)
|
||||||
|
implementation(libs.androidx.appcompat)
|
||||||
|
implementation(libs.material)
|
||||||
|
testImplementation(libs.junit)
|
||||||
|
androidTestImplementation(libs.androidx.junit)
|
||||||
|
androidTestImplementation(libs.androidx.espresso.core)
|
||||||
|
}
|
||||||
Vendored
+21
@@ -0,0 +1,21 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package nfs.mod.mpcore
|
||||||
|
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
import org.junit.Assert.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumented test, which will execute on an Android device.
|
||||||
|
*
|
||||||
|
* See [testing documentation](http://d.android.com/tools/testing).
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class ExampleInstrumentedTest {
|
||||||
|
@Test
|
||||||
|
fun useAppContext() {
|
||||||
|
// Context of the app under test.
|
||||||
|
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
|
assertEquals("nfs.mod.mpcore.test", appContext.packageName)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.22.1)
|
||||||
|
|
||||||
|
project("mpcore")
|
||||||
|
|
||||||
|
add_library(${CMAKE_PROJECT_NAME} SHARED
|
||||||
|
main.cpp
|
||||||
|
util/util.cpp
|
||||||
|
util/armhook.cpp)
|
||||||
|
|
||||||
|
target_link_libraries(${CMAKE_PROJECT_NAME}
|
||||||
|
android
|
||||||
|
log)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package nfs.mod.mpcore
|
||||||
|
|
||||||
|
object MultiplayerCore {
|
||||||
|
|
||||||
|
fun loadCore() = System.loadLibrary("mpcore")
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package nfs.mod.mpcore
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
import org.junit.Assert.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example local unit test, which will execute on the development machine (host).
|
||||||
|
*
|
||||||
|
* See [testing documentation](http://d.android.com/tools/testing).
|
||||||
|
*/
|
||||||
|
class ExampleUnitTest {
|
||||||
|
@Test
|
||||||
|
fun addition_isCorrect() {
|
||||||
|
assertEquals(4, 2 + 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -21,4 +21,4 @@ dependencyResolutionManagement {
|
|||||||
|
|
||||||
rootProject.name = "NFS Most Wanted"
|
rootProject.name = "NFS Most Wanted"
|
||||||
include(":app")
|
include(":app")
|
||||||
|
include(":mpcore")
|
||||||
|
|||||||
Reference in New Issue
Block a user