diff options
author | NeroBurner <pyro4hell@gmail.com> | 2021-06-21 21:51:42 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-21 20:51:42 +0100 |
commit | a7143c2a8c48b234d78ec666193b942ae0b62ca3 (patch) | |
tree | c6ba5317ba853536d25709f80aadc4036c14dfd3 /android/app | |
parent | 7fdbf3f231976257a1c246c6ebcdbc5bb8fa5571 (diff) | |
download | minetest-a7143c2a8c48b234d78ec666193b942ae0b62ca3.tar.gz minetest-a7143c2a8c48b234d78ec666193b942ae0b62ca3.tar.bz2 minetest-a7143c2a8c48b234d78ec666193b942ae0b62ca3.zip |
Move build/android directory to root of project (#11283)
Diffstat (limited to 'android/app')
-rw-r--r-- | android/app/build.gradle | 113 | ||||
-rw-r--r-- | android/app/src/main/AndroidManifest.xml | 62 | ||||
-rw-r--r-- | android/app/src/main/java/net/minetest/minetest/CopyZipTask.java | 82 | ||||
-rw-r--r-- | android/app/src/main/java/net/minetest/minetest/CustomEditText.java | 45 | ||||
-rw-r--r-- | android/app/src/main/java/net/minetest/minetest/GameActivity.java | 174 | ||||
-rw-r--r-- | android/app/src/main/java/net/minetest/minetest/MainActivity.java | 153 | ||||
-rw-r--r-- | android/app/src/main/java/net/minetest/minetest/UnzipService.java | 157 | ||||
-rw-r--r-- | android/app/src/main/res/drawable/background.png | bin | 0 -> 83 bytes | |||
-rw-r--r-- | android/app/src/main/res/drawable/bg.xml | 4 | ||||
-rw-r--r-- | android/app/src/main/res/layout/activity_main.xml | 30 | ||||
-rw-r--r-- | android/app/src/main/res/mipmap/ic_launcher.png | bin | 0 -> 5780 bytes | |||
-rw-r--r-- | android/app/src/main/res/values/strings.xml | 11 | ||||
-rw-r--r-- | android/app/src/main/res/values/styles.xml | 15 |
13 files changed, 846 insertions, 0 deletions
diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 000000000..b7d93ef0f --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,113 @@ +apply plugin: 'com.android.application' +android { + compileSdkVersion 29 + buildToolsVersion '30.0.3' + ndkVersion '22.0.7026061' + defaultConfig { + applicationId 'net.minetest.minetest' + minSdkVersion 16 + targetSdkVersion 29 + versionName "${versionMajor}.${versionMinor}.${versionPatch}" + versionCode project.versionCode + } + + // load properties + Properties props = new Properties() + def propfile = file('../local.properties') + if (propfile.exists()) + props.load(new FileInputStream(propfile)) + + if (props.getProperty('keystore') != null) { + signingConfigs { + release { + storeFile file(props['keystore']) + storePassword props['keystore.password'] + keyAlias props['key'] + keyPassword props['key.password'] + } + } + + buildTypes { + release { + minifyEnabled true + signingConfig signingConfigs.release + } + } + } + + // for multiple APKs + splits { + abi { + enable true + reset() + include 'armeabi-v7a', 'arm64-v8a' + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +task prepareAssets() { + def assetsFolder = "build/assets" + def projRoot = "../.." + def gameToCopy = "minetest_game" + + copy { + from "${projRoot}/minetest.conf.example", "${projRoot}/README.md" into assetsFolder + } + copy { + from "${projRoot}/doc/lgpl-2.1.txt" into "${assetsFolder}" + } + copy { + from "${projRoot}/builtin" into "${assetsFolder}/builtin" + } + copy { + from "${projRoot}/client/shaders" into "${assetsFolder}/client/shaders" + } + copy { + from "../native/deps/Android/Irrlicht/shaders" into "${assetsFolder}/client/shaders/Irrlicht" + } + copy { + from "${projRoot}/fonts" include "*.ttf" into "${assetsFolder}/fonts" + } + copy { + from "${projRoot}/games/${gameToCopy}" into "${assetsFolder}/games/${gameToCopy}" + } + /*copy { + // ToDo: fix broken locales + from "${projRoot}/po" into "${assetsFolder}/po" + }*/ + copy { + from "${projRoot}/textures" into "${assetsFolder}/textures" + } + + file("${assetsFolder}/.nomedia").text = ""; + + task zipAssets(type: Zip) { + archiveName "Minetest.zip" + from "${assetsFolder}" + destinationDir file("src/main/assets") + } +} + +preBuild.dependsOn zipAssets + +// Map for the version code that gives each ABI a value. +import com.android.build.OutputFile + +def abiCodes = ['armeabi-v7a': 0, 'arm64-v8a': 1] +android.applicationVariants.all { variant -> + variant.outputs.each { + output -> + def abiName = output.getFilter(OutputFile.ABI) + output.versionCodeOverride = abiCodes.get(abiName, 0) + variant.versionCode + } +} + +dependencies { + implementation project(':native') + implementation 'androidx.appcompat:appcompat:1.2.0' +} diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..fa93e7069 --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + package="net.minetest.minetest" + android:installLocation="auto"> + + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + + <!-- + `android:requestLegacyExternalStorage="true"` is workaround for using `/sdcard` + instead of the `getFilesDir()` patch for assets. Check link below for more information: + https://developer.android.com/training/data-storage/compatibility + --> + + <application + android:allowBackup="false" + android:icon="@mipmap/ic_launcher" + android:label="@string/label" + android:requestLegacyExternalStorage="true" + android:resizeableActivity="false" + tools:ignore="UnusedAttribute"> + + <meta-data + android:name="android.max_aspect" + android:value="3.0" /> + + <activity + android:name=".MainActivity" + android:configChanges="orientation|keyboardHidden|navigation|screenSize" + android:maxAspectRatio="3.0" + android:screenOrientation="sensorLandscape" + android:theme="@style/AppTheme"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <activity + android:name=".GameActivity" + android:configChanges="orientation|keyboard|keyboardHidden|navigation|screenSize|smallestScreenSize" + android:hardwareAccelerated="true" + android:launchMode="singleTask" + android:maxAspectRatio="3.0" + android:screenOrientation="sensorLandscape" + android:theme="@style/AppTheme"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + </intent-filter> + <meta-data + android:name="android.app.lib_name" + android:value="Minetest" /> + </activity> + + <service + android:name=".UnzipService" + android:enabled="true" + android:exported="false" /> + </application> + +</manifest> 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..bdf764138 --- /dev/null +++ b/android/app/src/main/java/net/minetest/minetest/GameActivity.java @@ -0,0 +1,174 @@ +/* +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.Button; +import android.widget.EditText; +import android.widget.LinearLayout; + +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); + LinearLayout container = new LinearLayout(this); + container.setOrientation(LinearLayout.VERTICAL); + builder.setView(container); + AlertDialog alertDialog = builder.create(); + EditText editText; + // For multi-line, do not close the dialog after pressing back button + if (editType == 1) { + editText = new EditText(this); + } else { + editText = new CustomEditText(this); + } + container.addView(editText); + editText.setMaxLines(8); + 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) -> { + // For multi-line, do not submit the text after pressing Enter key + if (keyCode == KeyEvent.KEYCODE_ENTER && editType != 1) { + imm.hideSoftInputFromWindow(editText.getWindowToken(), 0); + messageReturnCode = 0; + messageReturnValue = editText.getText().toString(); + alertDialog.dismiss(); + return true; + } + return false; + }); + // For multi-line, add Done button since Enter key does not submit text + if (editType == 1) { + Button doneButton = new Button(this); + container.addView(doneButton); + doneButton.setText(R.string.ime_dialog_done); + doneButton.setOnClickListener((view -> { + imm.hideSoftInputFromWindow(editText.getWindowToken(), 0); + messageReturnCode = 0; + messageReturnValue = editText.getText().toString(); + alertDialog.dismiss(); + })); + } + 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); + } +} diff --git a/android/app/src/main/res/drawable/background.png b/android/app/src/main/res/drawable/background.png Binary files differnew file mode 100644 index 000000000..43bd6089e --- /dev/null +++ b/android/app/src/main/res/drawable/background.png diff --git a/android/app/src/main/res/drawable/bg.xml b/android/app/src/main/res/drawable/bg.xml new file mode 100644 index 000000000..903335ed9 --- /dev/null +++ b/android/app/src/main/res/drawable/bg.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<bitmap xmlns:android="http://schemas.android.com/apk/res/android" + android:src="@drawable/background" + android:tileMode="repeat" /> diff --git a/android/app/src/main/res/layout/activity_main.xml b/android/app/src/main/res/layout/activity_main.xml new file mode 100644 index 000000000..e6f461f14 --- /dev/null +++ b/android/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,30 @@ +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/activity_main" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@drawable/bg"> + + <ProgressBar + android:id="@+id/progressBar" + style="@style/CustomProgressBar" + android:layout_width="match_parent" + android:layout_height="30dp" + android:layout_centerInParent="true" + android:layout_marginLeft="90dp" + android:layout_marginRight="90dp" + android:indeterminate="false" + android:max="100" + android:visibility="gone" /> + + <TextView + android:id="@+id/textView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/progressBar" + android:layout_centerInParent="true" + android:background="@android:color/transparent" + android:text="@string/loading" + android:textColor="#FEFEFE" + android:visibility="gone" /> + +</RelativeLayout> diff --git a/android/app/src/main/res/mipmap/ic_launcher.png b/android/app/src/main/res/mipmap/ic_launcher.png Binary files differnew file mode 100644 index 000000000..88a83782c --- /dev/null +++ b/android/app/src/main/res/mipmap/ic_launcher.png diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml new file mode 100644 index 000000000..85238117f --- /dev/null +++ b/android/app/src/main/res/values/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="label">Minetest</string> + <string name="loading">Loading…</string> + <string name="not_granted">Required permission wasn\'t granted, Minetest can\'t run without it</string> + <string name="notification_title">Loading Minetest</string> + <string name="notification_description">Less than 1 minute…</string> + <string name="ime_dialog_done">Done</string> + +</resources> diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000..291a4eaf1 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources xmlns:tools="http://schemas.android.com/tools"> + + <style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar"> + <item name="windowActionBar">false</item> + <item name="android:windowFullscreen">true</item> + <item name="android:windowBackground">@drawable/bg</item> + <item name="android:windowLayoutInDisplayCutoutMode" tools:targetApi="p">shortEdges</item> + </style> + + <style name="CustomProgressBar" parent="Widget.AppCompat.ProgressBar.Horizontal"> + <item name="android:indeterminateOnly">false</item> + </style> + +</resources> |