diff --git a/build/android/src/main/AndroidManifest.xml b/build/android/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..751587a4
--- /dev/null
+++ b/build/android/src/main/AndroidManifest.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build/android/src/main/java/mobi/MultiCraft/GameActivity.java b/build/android/src/main/java/mobi/MultiCraft/GameActivity.java
new file mode 100644
index 00000000..ddd4c7f9
--- /dev/null
+++ b/build/android/src/main/java/mobi/MultiCraft/GameActivity.java
@@ -0,0 +1,113 @@
+package mobi.MultiCraft;
+
+import android.app.NativeActivity;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.View;
+import android.view.WindowManager;
+
+public class GameActivity extends NativeActivity {
+ static {
+ System.loadLibrary("gmp");
+ System.loadLibrary("multicraft");
+ }
+
+ private int messageReturnCode;
+ private String messageReturnValue;
+
+ public static native void putMessageBoxResult(String text);
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+// startAd(this, true);
+ messageReturnCode = -1;
+ messageReturnValue = "";
+ makeFullScreen();
+ }
+
+
+ public void makeFullScreen() {
+ if (Build.VERSION.SDK_INT >= 19) {
+ this.getWindow().getDecorView()
+ .setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
+ }
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ if (hasFocus) {
+ makeFullScreen();
+ }
+ }
+
+ @Override
+ protected void onRestart() {
+ super.onRestart();
+// startAd(this, false);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ makeFullScreen();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+// stopAd();
+ }
+
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == 101) {
+ if (resultCode == RESULT_OK) {
+ String text = data.getStringExtra("text");
+ messageReturnCode = 0;
+ messageReturnValue = text;
+ } else {
+ messageReturnCode = 1;
+ }
+ }
+ }
+
+ public void copyAssets() {
+ }
+
+ public void showDialog(String acceptButton, String hint, String current, int editType) {
+ Intent intent = new Intent(this, InputDialogActivity.class);
+ Bundle params = new Bundle();
+ params.putString("acceptButton", acceptButton);
+ params.putString("hint", hint);
+ params.putString("current", current);
+ params.putInt("editType", editType);
+ intent.putExtras(params);
+ startActivityForResult(intent, 101);
+ messageReturnValue = "";
+ messageReturnCode = -1;
+ }
+
+ public int getDialogState() {
+ return messageReturnCode;
+ }
+
+ public String getDialogValue() {
+ messageReturnCode = -1;
+ return messageReturnValue;
+ }
+
+ public float getDensity() {
+ return getResources().getDisplayMetrics().density;
+ }
+
+ public int getDisplayWidth() {
+ return getResources().getDisplayMetrics().widthPixels;
+ }
+
+ public int getDisplayHeight() {
+ return getResources().getDisplayMetrics().heightPixels;
+ }
+}
\ No newline at end of file
diff --git a/build/android/src/main/java/mobi/MultiCraft/InputDialogActivity.java b/build/android/src/main/java/mobi/MultiCraft/InputDialogActivity.java
new file mode 100644
index 00000000..823dddd3
--- /dev/null
+++ b/build/android/src/main/java/mobi/MultiCraft/InputDialogActivity.java
@@ -0,0 +1,75 @@
+package mobi.MultiCraft;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.text.InputType;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnKeyListener;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+
+
+public class InputDialogActivity extends Activity {
+ private AlertDialog alertDialog;
+
+ @SuppressLint("InflateParams")
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Bundle b = getIntent().getExtras();
+ int editType = b.getInt("editType");
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ LayoutInflater inflater = this.getLayoutInflater();
+ View dialogView = inflater.inflate(R.layout.dialog, null);
+ builder.setView(dialogView);
+ final EditText editText = (EditText) dialogView.findViewById(R.id.editText);
+ final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);
+ if (editType == 3) {
+ editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ } else {
+ editText.setInputType(InputType.TYPE_CLASS_TEXT);
+ }
+ editText.setOnKeyListener(new OnKeyListener() {
+ @Override
+ public boolean onKey(View view, int KeyCode, KeyEvent event) {
+ if (KeyCode == KeyEvent.KEYCODE_ENTER) {
+ imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
+ pushResult(editText.getText().toString());
+ return true;
+ }
+ return false;
+ }
+ });
+ alertDialog = builder.create();
+ alertDialog.show();
+ alertDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ pushResult(editText.getText().toString());
+ setResult(Activity.RESULT_CANCELED);
+ alertDialog.dismiss();
+ finish();
+ }
+ });
+ }
+
+ public void pushResult(String text) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && text.matches(".*[А-я].*")) {
+ text = Transliteration.toLatin(text);
+ }
+ Intent resultData = new Intent();
+ resultData.putExtra("text", text);
+ setResult(Activity.RESULT_OK, resultData);
+ alertDialog.dismiss();
+ finish();
+ }
+}
\ No newline at end of file
diff --git a/build/android/src/main/java/mobi/MultiCraft/MainActivity.java b/build/android/src/main/java/mobi/MultiCraft/MainActivity.java
new file mode 100644
index 00000000..6dee623b
--- /dev/null
+++ b/build/android/src/main/java/mobi/MultiCraft/MainActivity.java
@@ -0,0 +1,370 @@
+package mobi.MultiCraft;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.StatFs;
+import android.support.annotation.NonNull;
+import android.support.v4.app.ActivityCompat;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.ProgressBar;
+import android.widget.Toast;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import static mobi.MultiCraft.PreferencesHelper.TAG_BUILD_NUMBER;
+import static mobi.MultiCraft.PreferencesHelper.TAG_SHORTCUT_CREATED;
+import static mobi.MultiCraft.PreferencesHelper.getBuildNumber;
+import static mobi.MultiCraft.PreferencesHelper.isCreateShortcut;
+import static mobi.MultiCraft.PreferencesHelper.loadSettings;
+import static mobi.MultiCraft.PreferencesHelper.saveSettings;
+
+public class MainActivity extends Activity {
+ public final static String TAG = "Error";
+ public final static String CREATE_SHORTCUT = "com.android.launcher.action.INSTALL_SHORTCUT";
+ public final static String FILES = Environment.getExternalStorageDirectory() + "/Files.zip";
+ public final static String WORLDS = Environment.getExternalStorageDirectory() + "/worlds.zip";
+ public final static String GAMES = Environment.getExternalStorageDirectory() + "/games.zip";
+ public final static String NOMEDIA = ".nomedia";
+ private final static int REQUEST_STORAGE = 0;
+ private ProgressDialog mProgressDialog;
+ private String dataFolder = "/Android/data/mobi.MultiCraft/files/";
+ private String unzipLocation = Environment.getExternalStorageDirectory() + dataFolder;
+ private ProgressBar mProgressBar;
+ private Utilities util;
+ private BroadcastReceiver myReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int progress = intent.getIntExtra(UnzipService.ACTION_PROGRESS, 0);
+ if (progress >= 0) {
+ mProgressBar.setVisibility(View.VISIBLE);
+ mProgressBar.setProgress(progress);
+ } else {
+ util.createNomedia();
+ runGame();
+ }
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ loadSettings(this);
+ IntentFilter filter = new IntentFilter(UnzipService.ACTION_UPDATE);
+ registerReceiver(myReceiver, filter);
+ if (!isTaskRoot()) {
+ finish();
+ return;
+ }
+
+
+ if (ActivityCompat.checkSelfPermission(this,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
+ requestStoragePermission();
+ } else {
+ init();
+ }
+ }
+
+ public void makeFullScreen() {
+ if (Build.VERSION.SDK_INT >= 19) {
+ this.getWindow().getDecorView()
+ .setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
+ }
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ if (hasFocus) {
+ makeFullScreen();
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ dismissProgressDialog();
+ unregisterReceiver(myReceiver);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ makeFullScreen();
+ }
+
+ private void addShortcut() {
+ saveSettings(TAG_SHORTCUT_CREATED, false);
+ Intent shortcutIntent = new Intent(getApplicationContext(), MainActivity.class);
+ shortcutIntent.setAction(Intent.ACTION_MAIN);
+ Intent addIntent = new Intent();
+ addIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
+ addIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, getString(R.string.app_name));
+ addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
+ Intent.ShortcutIconResource.fromContext(getApplicationContext(), R.drawable.ic_launcher));
+ addIntent.setAction(CREATE_SHORTCUT);
+ getApplicationContext().sendBroadcast(addIntent);
+ }
+
+ @SuppressWarnings("deprecation")
+ public void init() {
+ setContentView(R.layout.activity_main);
+ if (isCreateShortcut())
+ addShortcut();
+ mProgressBar = (ProgressBar) findViewById(R.id.PB1);
+ Drawable draw;
+ draw = getResources().getDrawable(R.drawable.custom_progress_bar);
+ mProgressBar.setProgressDrawable(draw);
+ util = new Utilities();
+ util.createDataFolder();
+ util.checkVersion();
+ }
+
+ private void requestPermissionAfterExplain() {
+ Toast.makeText(this, R.string.explain, Toast.LENGTH_LONG).show();
+ ActivityCompat.requestPermissions(MainActivity.this,
+ new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_STORAGE);
+ }
+
+ private void requestStoragePermission() {
+ if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
+ requestPermissionAfterExplain();
+ } else {
+ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
+ REQUEST_STORAGE);
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+ @NonNull int[] grantResults) {
+
+ // Check if the only required permission has been granted
+ if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ init();
+ } else {
+ requestStoragePermission();
+ }
+ }
+
+ private void showSpinnerDialog(int message) {
+ if (mProgressDialog == null) {
+ mProgressDialog = new ProgressDialog(MainActivity.this);
+ mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
+ mProgressDialog.setCancelable(false);
+ }
+ mProgressDialog.setMessage(getString(message));
+ mProgressDialog.show();
+ }
+
+ private void dismissProgressDialog() {
+ if (mProgressDialog != null && mProgressDialog.isShowing()) {
+ mProgressDialog.dismiss();
+ }
+ }
+
+ public void runGame() {
+ util.deleteZip(FILES);
+ util.deleteZip(WORLDS);
+ util.deleteZip(GAMES);
+ Intent intent = new Intent(MainActivity.this, GameActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ startActivity(intent);
+ }
+
+ private void startUnzipService(String[] file) throws IOException {
+ // Start MyIntentService
+ Intent intentMyIntentService = new Intent(this, UnzipService.class);
+ intentMyIntentService.putExtra(UnzipService.EXTRA_KEY_IN_FILE, file);
+ intentMyIntentService.putExtra(UnzipService.EXTRA_KEY_IN_LOCATION, unzipLocation);
+ startService(intentMyIntentService);
+
+ }
+
+ private class DeleteTask extends AsyncTask {
+ String location;
+
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ showSpinnerDialog(R.string.rm_old);
+ }
+
+ @Override
+ protected Void doInBackground(String... params) {
+ location = params[0];
+ for (String p : params) {
+ util.deleteFiles(p);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ if (isFinishing())
+ return;
+ dismissProgressDialog();
+ if (unzipLocation.equals(location)) {
+ new CopyZip().execute(FILES, WORLDS, GAMES);
+ } else {
+ new CopyZip().execute(FILES, GAMES);
+ }
+ }
+
+
+ }
+
+ private class CopyZip extends AsyncTask {
+ String[] zips;
+
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ }
+
+ @Override
+ protected String doInBackground(String... params) {
+ zips = params;
+ for (String s : zips) {
+ copyAssets(s);
+ }
+ return "Done";
+
+ }
+
+ @Override
+ protected void onPostExecute(String result) {
+ if (util.getAvailableSpaceInMB() > 15) {
+ try {
+ startUnzipService(zips);
+ } catch (IOException e) {
+ Log.e(TAG, "unzip failed: " + e.getMessage());
+ }
+ } else
+ Toast.makeText(MainActivity.this, R.string.not_enough_space, Toast.LENGTH_LONG).show();
+ }
+
+ private void copyAssets(String zipName) {
+ String filename = zipName.substring(zipName.lastIndexOf("/") + 1);
+ InputStream in;
+ OutputStream out;
+ try {
+ in = getAssets().open(filename);
+ out = new FileOutputStream(zipName);
+ copyFile(in, out);
+ in.close();
+ out.flush();
+ out.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to copy asset file: " + e.getMessage());
+ }
+ }
+
+ private void copyFile(InputStream in, OutputStream out) throws IOException {
+ byte[] buffer = new byte[1024];
+ int read;
+ while ((read = in.read(buffer)) != -1) {
+ out.write(buffer, 0, read);
+ }
+ }
+ }
+
+ private class Utilities {
+
+ private void createDataFolder() {
+ File folder = new File(unzipLocation);
+ if (!(folder.exists()))
+ folder.mkdirs();
+ }
+
+ private void deleteZip(String fileName) {
+ File file = new File(fileName);
+ if (file.exists())
+ file.delete();
+ }
+
+ private void startDeletion(boolean isAll) {
+ if (isAll) {
+ new DeleteTask().execute(unzipLocation);
+ } else {
+ new DeleteTask().execute(unzipLocation + "textures", unzipLocation + "games/MultiCraft", unzipLocation + "builtin",
+ unzipLocation + "fonts", unzipLocation + "debug.txt");
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ @SuppressLint("NewApi")
+ private long getAvailableSpaceInMB() {
+ final long SIZE_KB = 1024L;
+ final long SIZE_MB = SIZE_KB * SIZE_KB;
+ long availableSpace;
+ StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());
+ if (Build.VERSION.SDK_INT > 17) {
+ availableSpace = stat.getAvailableBlocksLong() * stat.getBlockSizeLong();
+ } else {
+ availableSpace = (long) stat.getAvailableBlocks() * (long) stat.getBlockSize();
+ }
+ return availableSpace / SIZE_MB;
+ }
+
+ private boolean isFolderEmpty(String folder) {
+ File location = new File(folder);
+ File[] contents = location.listFiles();
+ return contents == null || contents.length == 0;
+ }
+
+ public void checkVersion() {
+ if (isFolderEmpty(unzipLocation)) {
+ saveSettings(TAG_BUILD_NUMBER, getString(R.string.ver));
+ startDeletion(true);
+ } else if (getBuildNumber().equals(getString(R.string.ver))) {
+ runGame();
+ } else {
+ saveSettings(TAG_BUILD_NUMBER, getString(R.string.ver));
+ startDeletion(false);
+ }
+ }
+
+ private void deleteFiles(String path) {
+ File file = new File(path);
+ if (file.exists()) {
+ String deleteCmd = "rm -r " + path;
+ Runtime runtime = Runtime.getRuntime();
+ try {
+ runtime.exec(deleteCmd);
+ } catch (IOException e) {
+ Log.e(TAG, "delete files failed: " + e.getLocalizedMessage());
+ }
+ }
+ }
+
+ public void createNomedia() {
+ File myFile = new File(unzipLocation, NOMEDIA);
+ if (!myFile.exists())
+ try {
+ myFile.createNewFile();
+ } catch (IOException e) {
+ Log.e(TAG, "nomedia has not been created: " + e.getMessage());
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/build/android/src/main/java/mobi/MultiCraft/PreferencesHelper.java b/build/android/src/main/java/mobi/MultiCraft/PreferencesHelper.java
new file mode 100644
index 00000000..4329e215
--- /dev/null
+++ b/build/android/src/main/java/mobi/MultiCraft/PreferencesHelper.java
@@ -0,0 +1,40 @@
+package mobi.MultiCraft;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+public class PreferencesHelper {
+ public static final String SETTINGS = "settings";
+ public static final String TAG_SHORTCUT_CREATED = "createShortcut";
+ public static final String TAG_BUILD_NUMBER = "buildNumber";
+ private static boolean createShortcut;
+ private static String buildNumber;
+ private static SharedPreferences settings;
+
+ public static boolean isCreateShortcut() {
+ return createShortcut;
+ }
+
+ public static String getBuildNumber() {
+ return buildNumber;
+ }
+
+ public static void loadSettings(final Context context) {
+ settings = context.getSharedPreferences(SETTINGS, Context.MODE_PRIVATE);
+ createShortcut = settings.getBoolean(TAG_SHORTCUT_CREATED, true);
+ buildNumber = settings.getString(TAG_BUILD_NUMBER, "0");
+ }
+
+ public static void saveSettings(String tag, boolean bool) {
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putBoolean(tag, bool);
+ editor.apply();
+ }
+
+ public static void saveSettings(String tag, String value) {
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putString(tag, value);
+ editor.apply();
+ }
+
+}
\ No newline at end of file
diff --git a/build/android/src/main/java/mobi/MultiCraft/Transliteration.java b/build/android/src/main/java/mobi/MultiCraft/Transliteration.java
new file mode 100644
index 00000000..27ceeb1b
--- /dev/null
+++ b/build/android/src/main/java/mobi/MultiCraft/Transliteration.java
@@ -0,0 +1,72 @@
+package mobi.MultiCraft;
+
+public class Transliteration {
+
+ private static final String[] CHAR_TABLE = new String[81];
+ private static final char START_CHAR = 'Ё';
+
+
+ static {
+ CHAR_TABLE['А' - START_CHAR] = "A";
+ CHAR_TABLE['Б' - START_CHAR] = "B";
+ CHAR_TABLE['В' - START_CHAR] = "V";
+ CHAR_TABLE['Г' - START_CHAR] = "G";
+ CHAR_TABLE['Д' - START_CHAR] = "D";
+ CHAR_TABLE['Е' - START_CHAR] = "E";
+ CHAR_TABLE['Ё' - START_CHAR] = "E";
+ CHAR_TABLE['Ж' - START_CHAR] = "ZH";
+ CHAR_TABLE['З' - START_CHAR] = "Z";
+ CHAR_TABLE['И' - START_CHAR] = "I";
+ CHAR_TABLE['Й' - START_CHAR] = "J";
+ CHAR_TABLE['К' - START_CHAR] = "K";
+ CHAR_TABLE['Л' - START_CHAR] = "L";
+ CHAR_TABLE['М' - START_CHAR] = "M";
+ CHAR_TABLE['Н' - START_CHAR] = "N";
+ CHAR_TABLE['О' - START_CHAR] = "O";
+ CHAR_TABLE['П' - START_CHAR] = "P";
+ CHAR_TABLE['Р' - START_CHAR] = "R";
+ CHAR_TABLE['С' - START_CHAR] = "TS";
+ CHAR_TABLE['Т' - START_CHAR] = "T";
+ CHAR_TABLE['У' - START_CHAR] = "U";
+ CHAR_TABLE['Ф' - START_CHAR] = "F";
+ CHAR_TABLE['Х' - START_CHAR] = "H";
+ CHAR_TABLE['Ц' - START_CHAR] = "C";
+ CHAR_TABLE['Ч' - START_CHAR] = "CH";
+ CHAR_TABLE['Ш' - START_CHAR] = "SH";
+ CHAR_TABLE['Щ' - START_CHAR] = "SHCH";
+ CHAR_TABLE['Ъ' - START_CHAR] = "";
+ CHAR_TABLE['Ы' - START_CHAR] = "Y";
+ CHAR_TABLE['Ь' - START_CHAR] = "";
+ CHAR_TABLE['Э' - START_CHAR] = "E";
+ CHAR_TABLE['Ю' - START_CHAR] = "U";
+ CHAR_TABLE['Я' - START_CHAR] = "YA";
+
+ for (int i = 0; i < CHAR_TABLE.length; i++) {
+ char idx = (char) ((char) i + START_CHAR);
+ char lower = new String(new char[]{idx}).toLowerCase().charAt(0);
+ if (CHAR_TABLE[i] != null) {
+ CHAR_TABLE[lower - START_CHAR] = CHAR_TABLE[i].toLowerCase();
+ }
+ }
+ }
+
+ /**
+ * Переводит русский текст в транслит. В результирующей строке
+ * каждая русская буква будет заменена на соответствующую английскую.
+ * Не русские символы останутся прежними.
+ */
+ public static String toLatin(String text) {
+ char charBuffer[] = text.toCharArray();
+ StringBuilder sb = new StringBuilder(text.length());
+ for (char symbol : charBuffer) {
+ int i = symbol - START_CHAR;
+ if (i >= 0 && i < CHAR_TABLE.length) {
+ String replace = CHAR_TABLE[i];
+ sb.append(replace == null ? symbol : replace);
+ } else {
+ sb.append(symbol);
+ }
+ }
+ return sb.toString();
+ }
+}
diff --git a/build/android/src/main/java/mobi/MultiCraft/UnzipService.java b/build/android/src/main/java/mobi/MultiCraft/UnzipService.java
new file mode 100644
index 00000000..d3add5ab
--- /dev/null
+++ b/build/android/src/main/java/mobi/MultiCraft/UnzipService.java
@@ -0,0 +1,113 @@
+package mobi.MultiCraft;
+
+import android.app.IntentService;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
+
+public class UnzipService extends IntentService {
+ public static final String ACTION_UPDATE = "mobi.MultiCraft.UPDATE";
+ public static final String EXTRA_KEY_IN_FILE = "file";
+ public static final String EXTRA_KEY_IN_LOCATION = "location";
+ public static final String ACTION_PROGRESS = "progress";
+ public final String TAG = UnzipService.class.getSimpleName();
+ private NotificationManager mNotifyManager;
+ private int id = 1;
+
+ public UnzipService() {
+ super("mobi.MultiCraft.UnzipService");
+ }
+
+ private void isDir(String dir, String unzipLocation) {
+ File f = new File(unzipLocation + dir);
+
+ if (!f.isDirectory()) {
+ f.mkdirs();
+ }
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ Notification.Builder mBuilder = new Notification.Builder(this);
+ mBuilder.setContentTitle(getString(R.string.notification_title))
+ .setContentText(getString(R.string.notification_description)).setSmallIcon(R.drawable.update);
+ String[] file = intent.getStringArrayExtra(EXTRA_KEY_IN_FILE);
+ String location = intent.getStringExtra(EXTRA_KEY_IN_LOCATION);
+
+ mNotifyManager.notify(id, mBuilder.build());
+ int per = 0;
+ int size = getSummarySize(file);
+ for (String f : file) {
+ try {
+ try {
+ FileInputStream fin = new FileInputStream(f);
+ ZipInputStream zin = new ZipInputStream(fin);
+ ZipEntry ze;
+ while ((ze = zin.getNextEntry()) != null) {
+ if (ze.isDirectory()) {
+ per++;
+ isDir(ze.getName(), location);
+ } else {
+ per++;
+ int progress = 100 * per / size;
+ // send update
+ publishProgress(progress);
+ FileOutputStream f_out = new FileOutputStream(location + ze.getName());
+ byte[] buffer = new byte[8192];
+ int len;
+ while ((len = zin.read(buffer)) != -1) {
+ f_out.write(buffer, 0, len);
+ }
+ f_out.close();
+ zin.closeEntry();
+ f_out.close();
+ }
+ }
+ zin.close();
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, e.getMessage());
+ }
+ } catch (IOException e) {
+ Log.e(TAG, e.getLocalizedMessage());
+ }
+ }
+ }
+
+ private void publishProgress(int progress) {
+ Intent intentUpdate = new Intent(ACTION_UPDATE);
+ intentUpdate.putExtra(ACTION_PROGRESS, progress);
+ sendBroadcast(intentUpdate);
+ }
+
+ private int getSummarySize(String[] zips) {
+ int size = 0;
+ for (String z : zips) {
+ try {
+ ZipFile zipSize = new ZipFile(z);
+ size += zipSize.size();
+ } catch (IOException e) {
+ Log.e(TAG, e.getLocalizedMessage());
+ }
+ }
+ return size;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mNotifyManager.cancel(id);
+ publishProgress(-1);
+ }
+}
\ No newline at end of file
diff --git a/build/android/src/main/res/drawable-xhdpi/ic_launcher.png b/build/android/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..45760d62
Binary files /dev/null and b/build/android/src/main/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/build/android/src/main/res/drawable-xhdpi/update.png b/build/android/src/main/res/drawable-xhdpi/update.png
new file mode 100644
index 00000000..63fe67f2
Binary files /dev/null and b/build/android/src/main/res/drawable-xhdpi/update.png differ
diff --git a/build/android/src/main/res/drawable/background.png b/build/android/src/main/res/drawable/background.png
new file mode 100644
index 00000000..1ae046e1
Binary files /dev/null and b/build/android/src/main/res/drawable/background.png differ
diff --git a/build/android/src/main/res/drawable/bg.xml b/build/android/src/main/res/drawable/bg.xml
new file mode 100644
index 00000000..840f22ba
--- /dev/null
+++ b/build/android/src/main/res/drawable/bg.xml
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/build/android/src/main/res/drawable/custom_progress_bar.xml b/build/android/src/main/res/drawable/custom_progress_bar.xml
new file mode 100644
index 00000000..ec233f27
--- /dev/null
+++ b/build/android/src/main/res/drawable/custom_progress_bar.xml
@@ -0,0 +1,14 @@
+
+ -
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build/android/src/main/res/drawable/logo.png b/build/android/src/main/res/drawable/logo.png
new file mode 100644
index 00000000..218ebd01
Binary files /dev/null and b/build/android/src/main/res/drawable/logo.png differ
diff --git a/build/android/src/main/res/layout/activity_main.xml b/build/android/src/main/res/layout/activity_main.xml
new file mode 100644
index 00000000..a910225b
--- /dev/null
+++ b/build/android/src/main/res/layout/activity_main.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build/android/src/main/res/layout/dialog.xml b/build/android/src/main/res/layout/dialog.xml
new file mode 100644
index 00000000..9d0bd333
--- /dev/null
+++ b/build/android/src/main/res/layout/dialog.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/build/android/src/main/res/values-ru/strings.xml b/build/android/src/main/res/values-ru/strings.xml
new file mode 100644
index 00000000..4107373b
--- /dev/null
+++ b/build/android/src/main/res/values-ru/strings.xml
@@ -0,0 +1,11 @@
+
+
+
+
+ Подготовка к обновлению…
+ Загрузка…
+ Загрузка MultiCraft
+ Осталось меньше минуты…
+ Недостаточно места для распаковки.\nОсвободите пространство в памяти устройства!
+ Для корректной работы, игре требуется разрешение записывать в память устройтсва.
+
\ No newline at end of file
diff --git a/build/android/src/main/res/values-v21/styles.xml b/build/android/src/main/res/values-v21/styles.xml
new file mode 100644
index 00000000..2417cf7b
--- /dev/null
+++ b/build/android/src/main/res/values-v21/styles.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build/android/src/main/res/values/strings.xml b/build/android/src/main/res/values/strings.xml
new file mode 100644
index 00000000..bad627b6
--- /dev/null
+++ b/build/android/src/main/res/values/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ MultiCraft
+ 1.1.3
+ 312077575425
+
+
+ Preparing to update…
+ Loading…
+ Loading MultiCraft
+ Less than 1 minute…
+
+
+ Not enough space for unpack game data.\nPlease free some space on the storage memory!
+
+
+ Game need permission to write files to storage memory.
+
\ No newline at end of file
diff --git a/build/android/src/main/res/values/styles.xml b/build/android/src/main/res/values/styles.xml
new file mode 100644
index 00000000..a127de09
--- /dev/null
+++ b/build/android/src/main/res/values/styles.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
\ No newline at end of file