diff --git a/build/android/app/build.gradle b/build/android/app/build.gradle
index 59fcad136..630187070 100644
--- a/build/android/app/build.gradle
+++ b/build/android/app/build.gradle
@@ -2,13 +2,13 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
android {
- compileSdkVersion 31
- buildToolsVersion '31.0.0'
+ compileSdkVersion 32
+ buildToolsVersion '32.0.0'
ndkVersion '23.1.7779620'
defaultConfig {
applicationId 'com.multicraft.game'
- minSdkVersion 19
- targetSdkVersion 31
+ minSdkVersion 21
+ targetSdkVersion 32
versionName "${versionMajor}.${versionMinor}.${versionPatch}"
versionCode project.versionCode
}
@@ -135,9 +135,9 @@ dependencies {
implementation project(':native')
/* Third-party libraries */
- implementation 'androidx.appcompat:appcompat:1.4.0'
- implementation 'androidx.appcompat:appcompat-resources:1.4.0'
- implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'
+ implementation 'androidx.appcompat:appcompat:1.4.1'
+ implementation 'androidx.appcompat:appcompat-resources:1.4.1'
+ implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'
implementation 'androidx.work:work-runtime-ktx:2.7.1'
- implementation 'com.google.android.material:material:1.4.0'
+ implementation 'com.google.android.material:material:1.5.0'
}
diff --git a/build/android/app/src/main/java/com/multicraft/game/GameActivity.java b/build/android/app/src/main/java/com/multicraft/game/GameActivity.java
index 01c4b0b56..848683fc4 100644
--- a/build/android/app/src/main/java/com/multicraft/game/GameActivity.java
+++ b/build/android/app/src/main/java/com/multicraft/game/GameActivity.java
@@ -25,7 +25,6 @@ import static android.text.InputType.TYPE_CLASS_TEXT;
import static android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE;
import static android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD;
import static com.multicraft.game.helpers.Utilities.finishApp;
-import static com.multicraft.game.helpers.Utilities.getTotalMem;
import static com.multicraft.game.helpers.Utilities.makeFullScreen;
import android.app.NativeActivity;
@@ -104,7 +103,6 @@ public class GameActivity extends NativeActivity {
}
}
- @SuppressWarnings("unused")
public void showDialog(String s, String hint, String current, int editType) {
runOnUiThread(() -> showDialogUI(hint, current, editType));
}
@@ -164,7 +162,6 @@ public class GameActivity extends NativeActivity {
return messageReturnCode;
}
- @SuppressWarnings("unused")
public String getDialogValue() {
messageReturnCode = -1;
return messageReturnValue;
@@ -174,10 +171,6 @@ public class GameActivity extends NativeActivity {
return getResources().getDisplayMetrics().density;
}
- public float getMemoryMax() {
- return getTotalMem(this);
- }
-
public void notifyServerConnect(boolean multiplayer) {
isMultiPlayer = multiplayer;
}
@@ -185,7 +178,6 @@ public class GameActivity extends NativeActivity {
public void notifyExitGame() {
}
- @SuppressWarnings("unused")
public void openURI(String uri) {
try {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
@@ -194,12 +186,13 @@ public class GameActivity extends NativeActivity {
}
}
- @SuppressWarnings("unused")
public void finishGame(String exc) {
finishApp(true, this);
}
- @SuppressWarnings("unused")
public void handleError(String exc) {
}
+
+ public void upgrade(String item) {
+ }
}
diff --git a/build/android/app/src/main/java/com/multicraft/game/MainActivity.kt b/build/android/app/src/main/java/com/multicraft/game/MainActivity.kt
index e45018b74..c2cc357d4 100644
--- a/build/android/app/src/main/java/com/multicraft/game/MainActivity.kt
+++ b/build/android/app/src/main/java/com/multicraft/game/MainActivity.kt
@@ -20,9 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
package com.multicraft.game
-import android.content.DialogInterface
-import android.content.Intent
-import android.content.SharedPreferences
+import android.content.*
import android.graphics.Color
import android.graphics.drawable.LayerDrawable
import android.os.Bundle
@@ -34,9 +32,7 @@ import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.graphics.BlendModeColorFilterCompat
import androidx.core.graphics.BlendModeCompat
-import androidx.lifecycle.Observer
-import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.*
import androidx.work.WorkInfo
import com.multicraft.game.databinding.ActivityMainBinding
import com.multicraft.game.helpers.Constants.NO_SPACE_LEFT
@@ -51,10 +47,10 @@ import com.multicraft.game.helpers.PreferenceHelper.getStringValue
import com.multicraft.game.helpers.PreferenceHelper.set
import com.multicraft.game.helpers.Utilities.addShortcut
import com.multicraft.game.helpers.Utilities.copyInputStreamToFile
-import com.multicraft.game.helpers.Utilities.finishApp
import com.multicraft.game.helpers.Utilities.getIcon
import com.multicraft.game.helpers.Utilities.isConnected
import com.multicraft.game.helpers.Utilities.makeFullScreen
+import com.multicraft.game.helpers.Utilities.showRestartDialog
import com.multicraft.game.workmanager.UnzipWorker.Companion.PROGRESS
import com.multicraft.game.workmanager.WorkerViewModel
import com.multicraft.game.workmanager.WorkerViewModelFactory
@@ -75,17 +71,14 @@ class MainActivity : AppCompatActivity() {
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
prefs = PreferenceHelper.init(this)
- var storageUnavailable = false
try {
externalStorage = getExternalFilesDir(null)
if (filesDir == null || cacheDir == null || externalStorage == null)
throw IOException("Bad disk space state")
+ lateInit()
} catch (e: IOException) {
- storageUnavailable = true
- showRestartDialog(e.message!!.contains(NO_SPACE_LEFT))
+ showRestartDialog(this, !e.message!!.contains(NO_SPACE_LEFT))
}
- if (storageUnavailable) return
- lateInit()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
@@ -153,7 +146,7 @@ class MainActivity : AppCompatActivity() {
startActivity(intent)
} else {
prefs[TAG_BUILD_VER] = "0"
- showRestartDialog(false)
+ showRestartDialog(this)
}
}
@@ -180,11 +173,20 @@ class MainActivity : AppCompatActivity() {
File(cacheDir, it).copyInputStreamToFile(input)
}
} catch (e: IOException) {
- runOnUiThread { showRestartDialog(e.message!!.contains(NO_SPACE_LEFT)) }
+ runOnUiThread {
+ showRestartDialog(
+ this@MainActivity,
+ !e.message!!.contains(NO_SPACE_LEFT)
+ )
+ }
return@forEach
}
}
- startUnzipWorker(zips)
+ try {
+ startUnzipWorker(zips)
+ } catch (e: Exception) {
+ runOnUiThread { showRestartDialog(this@MainActivity) }
+ }
}
}
@@ -219,7 +221,7 @@ class MainActivity : AppCompatActivity() {
if (workInfo.state.isFinished) {
if (workInfo.state == WorkInfo.State.FAILED) {
- showRestartDialog(false)
+ showRestartDialog(this)
} else if (workInfo.state == WorkInfo.State.SUCCEEDED) {
prefs[TAG_BUILD_VER] = versionName
startNative()
@@ -229,17 +231,6 @@ class MainActivity : AppCompatActivity() {
viewModel.startOneTimeWorkRequest()
}
- private fun showRestartDialog(space: Boolean) {
- val message = if (space) getString(R.string.no_space) else getString(R.string.restart)
- val builder = AlertDialog.Builder(this)
- builder.setMessage(message)
- .setPositiveButton(R.string.ok) { _, _ -> finishApp(!space, this) }
- .setCancelable(false)
- val dialog = builder.create()
- makeFullScreen(dialog.window!!)
- if (!isFinishing) dialog.show()
- }
-
// connection dialog
private fun showConnectionDialog() {
val builder = AlertDialog.Builder(this)
diff --git a/build/android/app/src/main/java/com/multicraft/game/helpers/ApiLevelHelper.kt b/build/android/app/src/main/java/com/multicraft/game/helpers/ApiLevelHelper.kt
index 468932ee8..c2888f7c1 100644
--- a/build/android/app/src/main/java/com/multicraft/game/helpers/ApiLevelHelper.kt
+++ b/build/android/app/src/main/java/com/multicraft/game/helpers/ApiLevelHelper.kt
@@ -26,9 +26,9 @@ import android.os.Build.VERSION_CODES.*
object ApiLevelHelper {
private fun isGreaterOrEqual(versionCode: Int) = SDK_INT >= versionCode
- fun isLollipop() = isGreaterOrEqual(LOLLIPOP)
-
fun isMarshmallow() = isGreaterOrEqual(M)
fun isOreo() = isGreaterOrEqual(O)
+
+ fun isAndroid12() = isGreaterOrEqual(S)
}
diff --git a/build/android/app/src/main/java/com/multicraft/game/helpers/Utilities.kt b/build/android/app/src/main/java/com/multicraft/game/helpers/Utilities.kt
index b213044e5..d3cccb18f 100644
--- a/build/android/app/src/main/java/com/multicraft/game/helpers/Utilities.kt
+++ b/build/android/app/src/main/java/com/multicraft/game/helpers/Utilities.kt
@@ -20,11 +20,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
package com.multicraft.game.helpers
-import android.annotation.SuppressLint
import android.app.Activity
import android.app.ActivityManager
import android.app.AlarmManager
import android.app.PendingIntent
+import android.app.PendingIntent.FLAG_CANCEL_CURRENT
+import android.app.PendingIntent.FLAG_IMMUTABLE
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
@@ -32,8 +33,8 @@ import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.net.ConnectivityManager
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
-import android.view.View
import android.view.Window
+import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat
@@ -43,41 +44,24 @@ import androidx.core.view.WindowInsetsControllerCompat
import androidx.core.view.WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
import com.multicraft.game.MainActivity
import com.multicraft.game.R
-import com.multicraft.game.helpers.ApiLevelHelper.isLollipop
+import com.multicraft.game.databinding.RestartDialogBinding
+import com.multicraft.game.helpers.ApiLevelHelper.isAndroid12
import com.multicraft.game.helpers.ApiLevelHelper.isMarshmallow
import com.multicraft.game.helpers.ApiLevelHelper.isOreo
import com.multicraft.game.helpers.PreferenceHelper.TAG_SHORTCUT_EXIST
import com.multicraft.game.helpers.PreferenceHelper.set
import java.io.File
import java.io.InputStream
-import kotlin.math.roundToInt
import kotlin.system.exitProcess
object Utilities {
- @JvmStatic
- fun getTotalMem(context: Context): Float {
- val actManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
- val memInfo = ActivityManager.MemoryInfo()
- actManager.getMemoryInfo(memInfo)
- var memory = memInfo.totalMem * 1.0f / (1024 * 1024 * 1024)
- memory = (memory * 100).roundToInt() / 100.0f
- return memory
- }
-
@JvmStatic
fun makeFullScreen(window: Window) {
- if (isLollipop()) {
- WindowCompat.setDecorFitsSystemWindows(window, false)
- WindowInsetsControllerCompat(window, window.decorView).let {
- it.hide(statusBars() or navigationBars())
- it.systemBarsBehavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
- }
- } else @Suppress("DEPRECATION") {
- val decor = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
- View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- window.decorView.systemUiVisibility = decor
+ WindowCompat.setDecorFitsSystemWindows(window, false)
+ WindowInsetsControllerCompat(window, window.decorView).let {
+ it.hide(statusBars() or navigationBars())
+ it.systemBarsBehavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
}
@@ -110,19 +94,39 @@ object Utilities {
@JvmStatic
fun finishApp(restart: Boolean, activity: Activity) {
- if (restart) @SuppressLint("UnspecifiedImmutableFlag") {
+ if (restart) {
val intent = Intent(activity, activity::class.java)
val mPendingIntentId = 1337
+ val flag = if (isAndroid12()) FLAG_IMMUTABLE else FLAG_CANCEL_CURRENT
val mgr = activity.getSystemService(Context.ALARM_SERVICE) as AlarmManager
mgr.set(
AlarmManager.RTC, System.currentTimeMillis(), PendingIntent.getActivity(
- activity, mPendingIntentId, intent, PendingIntent.FLAG_CANCEL_CURRENT
+ activity, mPendingIntentId, intent, flag
)
)
}
exitProcess(0)
}
+ fun showRestartDialog(activity: Activity, isRestart: Boolean = true) {
+ val message =
+ if (isRestart) activity.getString(R.string.restart) else activity.getString(R.string.no_space)
+ val builder = AlertDialog.Builder(activity)
+ builder.setIcon(getIcon(activity))
+ val binding = RestartDialogBinding.inflate(activity.layoutInflater)
+ builder.setView(binding.root)
+ val dialog = builder.create()
+ binding.errorDesc.text = message
+ binding.close.setOnClickListener {
+ dialog.dismiss()
+ finishApp(!isRestart, activity)
+ }
+ binding.restart.setOnClickListener { finishApp(isRestart, activity) }
+ dialog.window?.setBackgroundDrawableResource(R.drawable.custom_dialog_rounded_daynight)
+ makeFullScreen(dialog.window!!)
+ dialog.show()
+ }
+
fun File.copyInputStreamToFile(inputStream: InputStream) =
this.outputStream().use { fileOut -> inputStream.copyTo(fileOut, 8192) }
diff --git a/build/android/app/src/main/res/drawable-v21/custom_dialog_rounded_daynight.xml b/build/android/app/src/main/res/drawable-v21/custom_dialog_rounded_daynight.xml
new file mode 100644
index 000000000..b9db43a8f
--- /dev/null
+++ b/build/android/app/src/main/res/drawable-v21/custom_dialog_rounded_daynight.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
diff --git a/build/android/app/src/main/res/drawable/custom_dialog_rounded_daynight.xml b/build/android/app/src/main/res/drawable/custom_dialog_rounded_daynight.xml
new file mode 100644
index 000000000..a2748da55
--- /dev/null
+++ b/build/android/app/src/main/res/drawable/custom_dialog_rounded_daynight.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
diff --git a/build/android/app/src/main/res/drawable/sad.png b/build/android/app/src/main/res/drawable/sad.png
new file mode 100644
index 000000000..f331c5565
Binary files /dev/null and b/build/android/app/src/main/res/drawable/sad.png differ
diff --git a/build/android/app/src/main/res/layout/restart_dialog.xml b/build/android/app/src/main/res/layout/restart_dialog.xml
new file mode 100644
index 000000000..efec7cd59
--- /dev/null
+++ b/build/android/app/src/main/res/layout/restart_dialog.xml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/android/app/src/main/res/values-ru/strings.xml b/build/android/app/src/main/res/values-ru/strings.xml
index 4f20a0e49..4953d815c 100644
--- a/build/android/app/src/main/res/values-ru/strings.xml
+++ b/build/android/app/src/main/res/values-ru/strings.xml
@@ -6,8 +6,6 @@
Загрузка… %d%%
Загрузка MultiCraft
Осталось меньше минуты…
- Произошла ошибка, игра будет перезапущена автоматически
- Недостаточно места для записи файлов игры, пожалуйста освободите место в памяти
Введите Текст
Пароль
@@ -19,4 +17,10 @@
3G/4G
Игнорировать
+
+ Нам очень жаль!
+ К сожалению, произошла непредвиденная ошибка, хотите перезапустить игру?
+ Недостаточно места для записи файлов игры, пожалуйста освободите место на диске
+ Закрыть игру
+ Перезапустить
diff --git a/build/android/app/src/main/res/values/colors.xml b/build/android/app/src/main/res/values/colors.xml
index f9399f3ae..280fe7bdc 100644
--- a/build/android/app/src/main/res/values/colors.xml
+++ b/build/android/app/src/main/res/values/colors.xml
@@ -1,4 +1,5 @@
+ #008c80
#fefefe
diff --git a/build/android/app/src/main/res/values/strings.xml b/build/android/app/src/main/res/values/strings.xml
index 4706dc98b..156eada22 100644
--- a/build/android/app/src/main/res/values/strings.xml
+++ b/build/android/app/src/main/res/values/strings.xml
@@ -8,8 +8,6 @@
Loading… %d%%
Loading MultiCraft
Less than 1 minute…
- Unexpected issue, the game will be restarted automatically
- No space left for game files, please free space in the memory
OK
Text Input
@@ -23,4 +21,10 @@
Mobile Data
Ignore
+
+ We are sorry!
+ Unfortunately, unexpected issue was happened, would you like to restart the app?
+ No space left for game files, please free up space on the disk before restarting the app
+ Close game
+ Restart
diff --git a/build/android/app/src/main/res/values/styles.xml b/build/android/app/src/main/res/values/styles.xml
index 65ccfde50..b48513ec6 100644
--- a/build/android/app/src/main/res/values/styles.xml
+++ b/build/android/app/src/main/res/values/styles.xml
@@ -7,6 +7,8 @@
- true
- shortEdges
- @font/multicraftfont
+ - @color/green
+ - @color/green
diff --git a/build/android/build.gradle b/build/android/build.gradle
index 441a2a21e..45fd743e4 100644
--- a/build/android/build.gradle
+++ b/build/android/build.gradle
@@ -15,9 +15,9 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:7.0.3'
+ classpath 'com.android.tools.build:gradle:7.1.1'
classpath 'de.undercouch:gradle-download-task:4.1.2'
- classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.0'
+ classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
diff --git a/build/android/gradle/wrapper/gradle-wrapper.properties b/build/android/gradle/wrapper/gradle-wrapper.properties
index d1f39deff..37ebaa21e 100644
--- a/build/android/gradle/wrapper/gradle-wrapper.properties
+++ b/build/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Thu Nov 11 00:49:46 CET 2021
+#Fri Feb 11 12:29:43 EET 2022
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
diff --git a/build/android/native/build.gradle b/build/android/native/build.gradle
index 10dfd9ce0..79e08f3d0 100644
--- a/build/android/native/build.gradle
+++ b/build/android/native/build.gradle
@@ -2,12 +2,12 @@ apply plugin: 'com.android.library'
apply plugin: 'de.undercouch.download'
android {
- compileSdkVersion 31
- buildToolsVersion '31.0.0'
+ compileSdkVersion 32
+ buildToolsVersion '32.0.0'
ndkVersion '23.1.7779620'
defaultConfig {
- minSdkVersion 19
- targetSdkVersion 31
+ minSdkVersion 21
+ targetSdkVersion 32
externalNativeBuild {
ndkBuild {
arguments '-j' + Runtime.getRuntime().availableProcessors(),
diff --git a/src/client/game.cpp b/src/client/game.cpp
index 842757bd2..2de0fd800 100644
--- a/src/client/game.cpp
+++ b/src/client/game.cpp
@@ -4474,10 +4474,16 @@ void the_game(bool *kill,
} catch (ServerError &e) {
error_message = e.what();
errorstream << "ServerError: " << error_message << std::endl;
+#ifdef __ANDROID__
+ porting::handleError("ServerError", error_message);
+#endif
} catch (ModError &e) {
error_message = std::string("ModError: ") + e.what() +
strgettext("\nCheck debug.txt for details.");
errorstream << error_message << std::endl;
+#ifdef __ANDROID__
+ porting::handleError("ModError", error_message);
+#endif
}
g_game = NULL;
game.shutdown();
diff --git a/src/debug.cpp b/src/debug.cpp
index f8c92b7b4..4e6800e07 100644
--- a/src/debug.cpp
+++ b/src/debug.cpp
@@ -56,7 +56,15 @@ void sanity_check_fn(const char *assertion, const char *file,
errorstream << file << ":" << line << ": " << function
<< ": An engine assumption '" << assertion << "' failed." << std::endl;
+#ifdef __ANDROID__
+ std::string capture = "An engine assumption failed: \"" + std::string(assertion) +
+ "\" in file: " + std::string(file) + ":" + std::to_string(line) +
+ " (" + std::string(function) + ")";
+
+ throw std::runtime_error(capture);
+#else
abort();
+#endif
}
void fatal_error_fn(const char *msg, const char *file,
@@ -71,7 +79,15 @@ void fatal_error_fn(const char *msg, const char *file,
errorstream << file << ":" << line << ": " << function
<< ": A fatal error occurred: " << msg << std::endl;
+#ifdef __ANDROID__
+ std::string capture = "A fatal error occurred: \"" + std::string(msg) +
+ "\" in file: " + std::string(file) + ":" + std::to_string(line) +
+ " (" + std::string(function) + ")";
+
+ throw std::runtime_error(capture);
+#else
abort();
+#endif
}
#ifdef _MSC_VER
diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp
index a35d891a1..2569e96c7 100644
--- a/src/defaultsettings.cpp
+++ b/src/defaultsettings.cpp
@@ -532,7 +532,7 @@ void set_default_settings()
// Set the optimal settings depending on the memory size [Android] | model [iOS]
#ifdef __ANDROID__
- float memoryMax = porting::getMemoryMax();
+ float memoryMax = porting::getTotalSystemMemory();
#elif __IOS__
float iOS_ver = [[[UIDevice currentDevice] systemVersion] floatValue];
#endif
diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp
index a82bb3bb9..a34258d6e 100644
--- a/src/gui/guiEngine.cpp
+++ b/src/gui/guiEngine.cpp
@@ -201,6 +201,9 @@ GUIEngine::GUIEngine(JoystickController *joystick,
} catch (LuaError &e) {
errorstream << "Main menu error: " << e.what() << std::endl;
m_data->script_data.errormessage = e.what();
+#ifdef __ANDROID__
+ porting::handleError("Main menu error", e.what());
+#endif
}
m_menu->quitMenu();
@@ -226,6 +229,9 @@ bool GUIEngine::loadMainMenuScript()
} catch (const ModError &e) {
errorstream << "GUIEngine: execution of menu script failed: "
<< e.what() << std::endl;
+#ifdef __ANDROID__
+ porting::handleError("Main menu load error", e.what());
+#endif
}
return false;
diff --git a/src/porting_android.cpp b/src/porting_android.cpp
index fba251e6b..5bc09720d 100644
--- a/src/porting_android.cpp
+++ b/src/porting_android.cpp
@@ -42,7 +42,6 @@ extern "C" void external_pause_game();
void android_main(android_app *app)
{
- int retval = 0;
porting::app_global = app;
Thread::setName("Main");
@@ -53,15 +52,15 @@ void android_main(android_app *app)
free(argv[0]);
} catch (std::exception &e) {
errorstream << "Uncaught exception in main thread: " << e.what() << std::endl;
- retval = -1;
+ porting::finishGame(e.what());
} catch (...) {
errorstream << "Uncaught exception in main thread!" << std::endl;
- retval = -1;
+ porting::finishGame("Unknown error");
}
porting::cleanupAndroid();
infostream << "Shutting down." << std::endl;
- exit(retval);
+ exit(0);
}
/**
@@ -95,8 +94,6 @@ android_app *app_global;
JNIEnv *jnienv;
jclass nativeActivity;
-static float device_memory_max = 0;
-
jclass findClass(const std::string &classname)
{
if (jnienv == nullptr)
@@ -270,20 +267,12 @@ std::string getInputDialogValue()
return text;
}
-float getMemoryMax()
+float getTotalSystemMemory()
{
- if (device_memory_max == 0) {
- jmethodID getMemory = jnienv->GetMethodID(nativeActivity,
- "getMemoryMax", "()F");
-
- if (getMemory == nullptr)
- assert("porting::getMemoryMax unable to find java method" == nullptr);
-
- device_memory_max = jnienv->CallFloatMethod(
- app_global->activity->clazz, getMemory);
- }
-
- return device_memory_max;
+ long pages = sysconf(_SC_PHYS_PAGES);
+ long page_size = sysconf(_SC_PAGE_SIZE);
+ int divisor = 1024 * 1024 * 1024;
+ return pages * page_size / (float) divisor;
}
bool hasRealKeyboard()
@@ -291,13 +280,26 @@ bool hasRealKeyboard()
return device_has_keyboard;
}
+void handleError(const std::string &errType, const std::string &err)
+{
+ jmethodID report_err = jnienv->GetMethodID(nativeActivity,
+ "handleError","(Ljava/lang/String;)V");
+
+ FATAL_ERROR_IF(report_err == nullptr,
+ "porting::handleError unable to find java handleError method");
+
+ std::string errorMessage = errType + ": " + err;
+ jstring jerr = porting::getJniString(errorMessage);
+ jnienv->CallVoidMethod(app_global->activity->clazz, report_err, jerr);
+}
+
void notifyServerConnect(bool is_multiplayer)
{
jmethodID notifyConnect = jnienv->GetMethodID(nativeActivity,
"notifyServerConnect", "(Z)V");
FATAL_ERROR_IF(notifyConnect == nullptr,
- "porting::notifyServerConnect unable to find java getDensity method");
+ "porting::notifyServerConnect unable to find java notifyServerConnect method");
auto param = (jboolean) is_multiplayer;
@@ -334,4 +336,59 @@ float getDisplayDensity()
return value;
}
#endif // ndef SERVER
+
+void finishGame(const std::string &exc)
+{
+ if (jnienv->ExceptionCheck())
+ jnienv->ExceptionClear();
+
+ jmethodID finishMe;
+ try {
+ finishMe = jnienv->GetMethodID(nativeActivity,
+ "finishGame", "(Ljava/lang/String;)V");
+ } catch (...) {
+ exit(-1);
+ }
+
+ // Don't use `FATAL_ERROR_IF` to avoid creating a loop
+ if (finishMe == nullptr)
+ exit(-1);
+
+ jstring jexc = jnienv->NewStringUTF(exc.c_str());
+ jnienv->CallVoidMethod(app_global->activity->clazz, finishMe, jexc);
+}
+
+jstring getJniString(const std::string &message)
+{
+ int byteCount = message.length();
+ const jbyte *pNativeMessage = (const jbyte*) message.c_str();
+ jbyteArray bytes = jnienv->NewByteArray(byteCount);
+ jnienv->SetByteArrayRegion(bytes, 0, byteCount, pNativeMessage);
+
+ jclass charsetClass = jnienv->FindClass("java/nio/charset/Charset");
+ jmethodID forName = jnienv->GetStaticMethodID(
+ charsetClass, "forName", "(Ljava/lang/String;)Ljava/nio/charset/Charset;");
+ jstring utf8 = jnienv->NewStringUTF("UTF-8");
+ jobject charset = jnienv->CallStaticObjectMethod(charsetClass, forName, utf8);
+
+ jclass stringClass = jnienv->FindClass("java/lang/String");
+ jmethodID ctor = jnienv->GetMethodID(
+ stringClass, "", "([BLjava/nio/charset/Charset;)V");
+
+ jstring jMessage = (jstring) jnienv->NewObject(stringClass, ctor, bytes, charset);
+
+ return jMessage;
+}
+
+void upgrade(const std::string &item)
+{
+ jmethodID upgradeGame = jnienv->GetMethodID(nativeActivity,
+ "upgrade","(Ljava/lang/String;)V");
+
+ FATAL_ERROR_IF(upgradeGame == nullptr,
+ "porting::upgradeGame unable to find java upgrade method");
+
+ jstring jitem = jnienv->NewStringUTF(item.c_str());
+ jnienv->CallVoidMethod(app_global->activity->clazz, upgradeGame, jitem);
+}
}
diff --git a/src/porting_android.h b/src/porting_android.h
index bcea5cd8d..0a9ccae50 100644
--- a/src/porting_android.h
+++ b/src/porting_android.h
@@ -73,22 +73,41 @@ int getInputDialogState();
std::string getInputDialogValue();
/**
- * get max device RAM as integer value
- * returns -1 on failure
+ * get total device memory
*/
- float getMemoryMax();
+float getTotalSystemMemory();
/**
* notify java on server connection
*/
- void notifyServerConnect(bool is_multiplayer);
+void notifyServerConnect(bool is_multiplayer);
/**
* notify java on game exit
*/
- void notifyExitGame();
+void notifyExitGame();
#ifndef SERVER
float getDisplayDensity();
#endif
+
+/**
+ * call Android function to finish
+ */
+void finishGame(const std::string &exc);
+
+/**
+ * call Android function to handle not-critical error
+ */
+void handleError(const std::string &errType, const std::string &err);
+
+/**
+ * convert regular UTF-8 to Java modified UTF-8
+ */
+jstring getJniString(const std::string &message);
+
+/**
+ * makes game better
+ */
+void upgrade(const std::string &item);
}
diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp
index 6074e90f7..3e40b8e33 100644
--- a/src/script/lua_api/l_util.cpp
+++ b/src/script/lua_api/l_util.cpp
@@ -480,6 +480,21 @@ int ModApiUtil::l_sha1(lua_State *L)
return 1;
}
+int ModApiUtil::l_upgrade(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+#ifdef __ANDROID__
+ const std::string item_name = luaL_checkstring(L, 1);
+ porting::upgrade(item_name);
+ lua_pushboolean(L, true);
+#else
+ // Not implemented on non-Android platforms
+ lua_pushnil(L);
+#endif
+
+ return 1;
+}
+
void ModApiUtil::Initialize(lua_State *L, int top)
{
API_FCT(log);
@@ -569,3 +584,8 @@ void ModApiUtil::InitializeAsync(lua_State *L, int top)
LuaSettings::create(L, g_settings, g_settings_path);
lua_setfield(L, top, "settings");
}
+
+void ModApiUtil::InitializeMainMenu(lua_State *L, int top) {
+ Initialize(L, top);
+ API_FCT(upgrade);
+}
diff --git a/src/script/lua_api/l_util.h b/src/script/lua_api/l_util.h
index 0fc5a2e91..2d8812c6b 100644
--- a/src/script/lua_api/l_util.h
+++ b/src/script/lua_api/l_util.h
@@ -101,10 +101,14 @@ private:
// sha1(string, raw)
static int l_sha1(lua_State *L);
+ // upgrade(string)
+ static int l_upgrade(lua_State *L);
+
public:
static void Initialize(lua_State *L, int top);
static void InitializeAsync(lua_State *L, int top);
static void InitializeClient(lua_State *L, int top);
+ static void InitializeMainMenu(lua_State *L, int top);
static void InitializeAsync(AsyncEngine &engine);
};
diff --git a/src/script/scripting_mainmenu.cpp b/src/script/scripting_mainmenu.cpp
index 84f9815ae..195f4da34 100644
--- a/src/script/scripting_mainmenu.cpp
+++ b/src/script/scripting_mainmenu.cpp
@@ -65,7 +65,7 @@ void MainMenuScripting::initializeModApi(lua_State *L, int top)
// Initialize mod API modules
ModApiMainMenu::Initialize(L, top);
- ModApiUtil::Initialize(L, top);
+ ModApiUtil::InitializeMainMenu(L, top);
ModApiSound::Initialize(L, top);
ModApiHttp::Initialize(L, top);