aboutsummaryrefslogtreecommitdiff
path: root/android/app/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'android/app/src/main/java')
-rw-r--r--android/app/src/main/java/net/minetest/minetest/CopyZipTask.java82
-rw-r--r--android/app/src/main/java/net/minetest/minetest/CustomEditText.java45
-rw-r--r--android/app/src/main/java/net/minetest/minetest/GameActivity.java149
-rw-r--r--android/app/src/main/java/net/minetest/minetest/MainActivity.java153
-rw-r--r--android/app/src/main/java/net/minetest/minetest/UnzipService.java157
5 files changed, 586 insertions, 0 deletions
diff --git a/android/app/src/main/java/net/minetest/minetest/CopyZipTask.java b/android/app/src/main/java/net/minetest/minetest/CopyZipTask.java
new file mode 100644
index 000000000..6d4b6ab0f
--- /dev/null
+++ b/android/app/src/main/java/net/minetest/minetest/CopyZipTask.java
@@ -0,0 +1,82 @@
+/*
+Minetest
+Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik <MoNTE48@mail.ua>
+Copyright (C) 2014-2020 ubulem, Bektur Mambetov <berkut87@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+package net.minetest.minetest;
+
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.ref.WeakReference;
+
+public class CopyZipTask extends AsyncTask<String, Void, String> {
+
+ private final WeakReference<AppCompatActivity> activityRef;
+
+ CopyZipTask(AppCompatActivity activity) {
+ activityRef = new WeakReference<>(activity);
+ }
+
+ protected String doInBackground(String... params) {
+ copyAsset(params[0]);
+ return params[0];
+ }
+
+ @Override
+ protected void onPostExecute(String result) {
+ startUnzipService(result);
+ }
+
+ private void copyAsset(String zipName) {
+ String filename = zipName.substring(zipName.lastIndexOf("/") + 1);
+ try (InputStream in = activityRef.get().getAssets().open(filename);
+ OutputStream out = new FileOutputStream(zipName)) {
+ copyFile(in, out);
+ } catch (IOException e) {
+ AppCompatActivity activity = activityRef.get();
+ if (activity != null) {
+ activity.runOnUiThread(() -> Toast.makeText(activityRef.get(), e.getLocalizedMessage(), Toast.LENGTH_LONG).show());
+ }
+ cancel(true);
+ }
+ }
+
+ 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 void startUnzipService(String file) {
+ Intent intent = new Intent(activityRef.get(), UnzipService.class);
+ intent.putExtra(UnzipService.EXTRA_KEY_IN_FILE, file);
+ AppCompatActivity activity = activityRef.get();
+ if (activity != null) {
+ activity.startService(intent);
+ }
+ }
+}
diff --git a/android/app/src/main/java/net/minetest/minetest/CustomEditText.java b/android/app/src/main/java/net/minetest/minetest/CustomEditText.java
new file mode 100644
index 000000000..8d0a503d0
--- /dev/null
+++ b/android/app/src/main/java/net/minetest/minetest/CustomEditText.java
@@ -0,0 +1,45 @@
+/*
+Minetest
+Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik <MoNTE48@mail.ua>
+Copyright (C) 2014-2020 ubulem, Bektur Mambetov <berkut87@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+package net.minetest.minetest;
+
+import android.content.Context;
+import android.view.KeyEvent;
+import android.view.inputmethod.InputMethodManager;
+
+import androidx.appcompat.widget.AppCompatEditText;
+
+import java.util.Objects;
+
+public class CustomEditText extends AppCompatEditText {
+ public CustomEditText(Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ InputMethodManager mgr = (InputMethodManager)
+ getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+ Objects.requireNonNull(mgr).hideSoftInputFromWindow(this.getWindowToken(), 0);
+ }
+ return false;
+ }
+}
diff --git a/android/app/src/main/java/net/minetest/minetest/GameActivity.java b/android/app/src/main/java/net/minetest/minetest/GameActivity.java
new file mode 100644
index 000000000..38a388230
--- /dev/null
+++ b/android/app/src/main/java/net/minetest/minetest/GameActivity.java
@@ -0,0 +1,149 @@
+/*
+Minetest
+Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik <MoNTE48@mail.ua>
+Copyright (C) 2014-2020 ubulem, Bektur Mambetov <berkut87@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+package net.minetest.minetest;
+
+import android.app.NativeActivity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.text.InputType;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+
+import androidx.appcompat.app.AlertDialog;
+
+import java.util.Objects;
+
+public class GameActivity extends NativeActivity {
+ static {
+ System.loadLibrary("c++_shared");
+ System.loadLibrary("Minetest");
+ }
+
+ private int messageReturnCode = -1;
+ 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);
+ }
+
+ private void makeFullScreen() {
+ if (Build.VERSION.SDK_INT >= 19)
+ this.getWindow().getDecorView().setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
+ 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 onResume() {
+ super.onResume();
+ makeFullScreen();
+ }
+
+ @Override
+ public void onBackPressed() {
+ // Ignore the back press so Minetest can handle it
+ }
+
+ public void showDialog(String acceptButton, String hint, String current, int editType) {
+ runOnUiThread(() -> showDialogUI(hint, current, editType));
+ }
+
+ private void showDialogUI(String hint, String current, int editType) {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ EditText editText = new CustomEditText(this);
+ builder.setView(editText);
+ AlertDialog alertDialog = builder.create();
+ editText.requestFocus();
+ editText.setHint(hint);
+ editText.setText(current);
+ final InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
+ Objects.requireNonNull(imm).toggleSoftInput(InputMethodManager.SHOW_FORCED,
+ InputMethodManager.HIDE_IMPLICIT_ONLY);
+ if (editType == 1)
+ editText.setInputType(InputType.TYPE_CLASS_TEXT |
+ InputType.TYPE_TEXT_FLAG_MULTI_LINE);
+ else if (editType == 3)
+ editText.setInputType(InputType.TYPE_CLASS_TEXT |
+ InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ else
+ editText.setInputType(InputType.TYPE_CLASS_TEXT);
+ editText.setSelection(editText.getText().length());
+ editText.setOnKeyListener((view, KeyCode, event) -> {
+ if (KeyCode == KeyEvent.KEYCODE_ENTER) {
+ imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
+ messageReturnCode = 0;
+ messageReturnValue = editText.getText().toString();
+ alertDialog.dismiss();
+ return true;
+ }
+ return false;
+ });
+ alertDialog.show();
+ alertDialog.setOnCancelListener(dialog -> {
+ getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
+ messageReturnValue = current;
+ messageReturnCode = -1;
+ });
+ }
+
+ public int getDialogState() {
+ return messageReturnCode;
+ }
+
+ public String getDialogValue() {
+ messageReturnCode = -1;
+ return messageReturnValue;
+ }
+
+ public float getDensity() {
+ return getResources().getDisplayMetrics().density;
+ }
+
+ public int getDisplayHeight() {
+ return getResources().getDisplayMetrics().heightPixels;
+ }
+
+ public int getDisplayWidth() {
+ return getResources().getDisplayMetrics().widthPixels;
+ }
+
+ public void openURI(String uri) {
+ Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
+ startActivity(browserIntent);
+ }
+}
diff --git a/android/app/src/main/java/net/minetest/minetest/MainActivity.java b/android/app/src/main/java/net/minetest/minetest/MainActivity.java
new file mode 100644
index 000000000..2aa50d9ad
--- /dev/null
+++ b/android/app/src/main/java/net/minetest/minetest/MainActivity.java
@@ -0,0 +1,153 @@
+/*
+Minetest
+Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik <MoNTE48@mail.ua>
+Copyright (C) 2014-2020 ubulem, Bektur Mambetov <berkut87@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+package net.minetest.minetest;
+
+import android.Manifest;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static net.minetest.minetest.UnzipService.ACTION_FAILURE;
+import static net.minetest.minetest.UnzipService.ACTION_PROGRESS;
+import static net.minetest.minetest.UnzipService.ACTION_UPDATE;
+import static net.minetest.minetest.UnzipService.FAILURE;
+import static net.minetest.minetest.UnzipService.SUCCESS;
+
+public class MainActivity extends AppCompatActivity {
+ private final static int versionCode = BuildConfig.VERSION_CODE;
+ private final static int PERMISSIONS = 1;
+ private static final String[] REQUIRED_SDK_PERMISSIONS =
+ new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
+ private static final String SETTINGS = "MinetestSettings";
+ private static final String TAG_VERSION_CODE = "versionCode";
+ private ProgressBar mProgressBar;
+ private TextView mTextView;
+ private SharedPreferences sharedPreferences;
+ private final BroadcastReceiver myReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int progress = 0;
+ if (intent != null)
+ progress = intent.getIntExtra(ACTION_PROGRESS, 0);
+ if (progress >= 0) {
+ if (mProgressBar != null) {
+ mProgressBar.setVisibility(View.VISIBLE);
+ mProgressBar.setProgress(progress);
+ }
+ mTextView.setVisibility(View.VISIBLE);
+ } else if (progress == FAILURE) {
+ Toast.makeText(MainActivity.this, intent.getStringExtra(ACTION_FAILURE), Toast.LENGTH_LONG).show();
+ finish();
+ } else if (progress == SUCCESS)
+ startNative();
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ IntentFilter filter = new IntentFilter(ACTION_UPDATE);
+ registerReceiver(myReceiver, filter);
+ mProgressBar = findViewById(R.id.progressBar);
+ mTextView = findViewById(R.id.textView);
+ sharedPreferences = getSharedPreferences(SETTINGS, Context.MODE_PRIVATE);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
+ checkPermission();
+ else
+ checkAppVersion();
+ }
+
+ private void checkPermission() {
+ final List<String> missingPermissions = new ArrayList<>();
+ for (final String permission : REQUIRED_SDK_PERMISSIONS) {
+ final int result = ContextCompat.checkSelfPermission(this, permission);
+ if (result != PackageManager.PERMISSION_GRANTED)
+ missingPermissions.add(permission);
+ }
+ if (!missingPermissions.isEmpty()) {
+ final String[] permissions = missingPermissions
+ .toArray(new String[0]);
+ ActivityCompat.requestPermissions(this, permissions, PERMISSIONS);
+ } else {
+ final int[] grantResults = new int[REQUIRED_SDK_PERMISSIONS.length];
+ Arrays.fill(grantResults, PackageManager.PERMISSION_GRANTED);
+ onRequestPermissionsResult(PERMISSIONS, REQUIRED_SDK_PERMISSIONS, grantResults);
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode,
+ @NonNull String[] permissions, @NonNull int[] grantResults) {
+ if (requestCode == PERMISSIONS) {
+ for (int grantResult : grantResults) {
+ if (grantResult != PackageManager.PERMISSION_GRANTED) {
+ Toast.makeText(this, R.string.not_granted, Toast.LENGTH_LONG).show();
+ finish();
+ }
+ }
+ checkAppVersion();
+ }
+ }
+
+ private void checkAppVersion() {
+ if (sharedPreferences.getInt(TAG_VERSION_CODE, 0) == versionCode)
+ startNative();
+ else
+ new CopyZipTask(this).execute(getCacheDir() + "/Minetest.zip");
+ }
+
+ private void startNative() {
+ sharedPreferences.edit().putInt(TAG_VERSION_CODE, versionCode).apply();
+ Intent intent = new Intent(this, GameActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ startActivity(intent);
+ }
+
+ @Override
+ public void onBackPressed() {
+ // Prevent abrupt interruption when copy game files from assets
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ unregisterReceiver(myReceiver);
+ }
+}
diff --git a/android/app/src/main/java/net/minetest/minetest/UnzipService.java b/android/app/src/main/java/net/minetest/minetest/UnzipService.java
new file mode 100644
index 000000000..b69f7f36e
--- /dev/null
+++ b/android/app/src/main/java/net/minetest/minetest/UnzipService.java
@@ -0,0 +1,157 @@
+/*
+Minetest
+Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik <MoNTE48@mail.ua>
+Copyright (C) 2014-2020 ubulem, Bektur Mambetov <berkut87@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+package net.minetest.minetest;
+
+import android.app.IntentService;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Environment;
+import android.widget.Toast;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+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 = "net.minetest.minetest.UPDATE";
+ public static final String ACTION_PROGRESS = "net.minetest.minetest.PROGRESS";
+ public static final String ACTION_FAILURE = "net.minetest.minetest.FAILURE";
+ public static final String EXTRA_KEY_IN_FILE = "file";
+ public static final int SUCCESS = -1;
+ public static final int FAILURE = -2;
+ private final int id = 1;
+ private NotificationManager mNotifyManager;
+ private boolean isSuccess = true;
+ private String failureMessage;
+
+ public UnzipService() {
+ super("net.minetest.minetest.UnzipService");
+ }
+
+ private void isDir(String dir, String location) {
+ File f = new File(location, dir);
+ if (!f.isDirectory())
+ f.mkdirs();
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ createNotification();
+ unzip(intent);
+ }
+
+ private void createNotification() {
+ String name = "net.minetest.minetest";
+ String channelId = "Minetest channel";
+ String description = "notifications from Minetest";
+ Notification.Builder builder;
+ if (mNotifyManager == null)
+ mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ int importance = NotificationManager.IMPORTANCE_LOW;
+ NotificationChannel mChannel = null;
+ if (mNotifyManager != null)
+ mChannel = mNotifyManager.getNotificationChannel(channelId);
+ if (mChannel == null) {
+ mChannel = new NotificationChannel(channelId, name, importance);
+ mChannel.setDescription(description);
+ // Configure the notification channel, NO SOUND
+ mChannel.setSound(null, null);
+ mChannel.enableLights(false);
+ mChannel.enableVibration(false);
+ mNotifyManager.createNotificationChannel(mChannel);
+ }
+ builder = new Notification.Builder(this, channelId);
+ } else {
+ builder = new Notification.Builder(this);
+ }
+ builder.setContentTitle(getString(R.string.notification_title))
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .setContentText(getString(R.string.notification_description));
+ mNotifyManager.notify(id, builder.build());
+ }
+
+ private void unzip(Intent intent) {
+ String zip = intent.getStringExtra(EXTRA_KEY_IN_FILE);
+ isDir("Minetest", Environment.getExternalStorageDirectory().toString());
+ String location = Environment.getExternalStorageDirectory() + File.separator + "Minetest" + File.separator;
+ int per = 0;
+ int size = getSummarySize(zip);
+ File zipFile = new File(zip);
+ int readLen;
+ byte[] readBuffer = new byte[8192];
+ try (FileInputStream fileInputStream = new FileInputStream(zipFile);
+ ZipInputStream zipInputStream = new ZipInputStream(fileInputStream)) {
+ ZipEntry ze;
+ while ((ze = zipInputStream.getNextEntry()) != null) {
+ if (ze.isDirectory()) {
+ ++per;
+ isDir(ze.getName(), location);
+ } else {
+ publishProgress(100 * ++per / size);
+ try (OutputStream outputStream = new FileOutputStream(location + ze.getName())) {
+ while ((readLen = zipInputStream.read(readBuffer)) != -1) {
+ outputStream.write(readBuffer, 0, readLen);
+ }
+ }
+ }
+ zipFile.delete();
+ }
+ } catch (IOException e) {
+ isSuccess = false;
+ failureMessage = e.getLocalizedMessage();
+ }
+ }
+
+ private void publishProgress(int progress) {
+ Intent intentUpdate = new Intent(ACTION_UPDATE);
+ intentUpdate.putExtra(ACTION_PROGRESS, progress);
+ if (!isSuccess) intentUpdate.putExtra(ACTION_FAILURE, failureMessage);
+ sendBroadcast(intentUpdate);
+ }
+
+ private int getSummarySize(String zip) {
+ int size = 0;
+ try {
+ ZipFile zipSize = new ZipFile(zip);
+ size += zipSize.size();
+ } catch (IOException e) {
+ Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_LONG).show();
+ }
+ return size;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mNotifyManager.cancel(id);
+ publishProgress(isSuccess ? SUCCESS : FAILURE);
+ }
+}