]> git.lizzy.rs Git - dragonfireclient.git/commitdiff
Use scoped app storage on Android (#11466)
authorrubenwardy <rw@rubenwardy.com>
Fri, 15 Oct 2021 16:14:48 +0000 (17:14 +0100)
committerGitHub <noreply@github.com>
Fri, 15 Oct 2021 16:14:48 +0000 (18:14 +0200)
From November 2021, the Play Store will no longer be accepting
apps which use the deprecated getExternalStorageDirectory() API.

Therefore, this commit replaces uses of deprecated API with the new
scoped API (`getExternalFilesDir()` and `getExternalCacheDir()`).
It also provides a temporary migration to move user data from the
shared external directory to new storage.

Fixes #2097,  #11417 and #11118

.clang-format
android/app/src/main/java/net/minetest/minetest/CopyZipTask.java [deleted file]
android/app/src/main/java/net/minetest/minetest/GameActivity.java
android/app/src/main/java/net/minetest/minetest/MainActivity.java
android/app/src/main/java/net/minetest/minetest/UnzipService.java
android/app/src/main/java/net/minetest/minetest/Utils.java [new file with mode: 0644]
android/app/src/main/res/layout/activity_main.xml
android/app/src/main/res/values/strings.xml
android/native/build.gradle
src/porting_android.cpp

index 0db8ab1671aac78b3d93144dd666613cb9da52fd..63f12b6c42adefca9fe9a5d7e5b34360ae79e018 100644 (file)
@@ -1,6 +1,7 @@
 BasedOnStyle: LLVM
-IndentWidth: 8
+IndentWidth: 4
 UseTab: Always
+TabWidth: 4
 BreakBeforeBraces: Custom
 Standard: Cpp11
 BraceWrapping:
@@ -16,7 +17,7 @@ BraceWrapping:
 FixNamespaceComments: false
 AllowShortIfStatementsOnASingleLine: false
 IndentCaseLabels: false
-AccessModifierOffset: -8
+AccessModifierOffset: -4
 ColumnLimit: 90
 AllowShortFunctionsOnASingleLine: InlineOnly
 SortIncludes: false
@@ -26,7 +27,7 @@ IncludeCategories:
   - Regex:           '^<.*'
     Priority:        1
 AlignAfterOpenBracket: DontAlign
-ContinuationIndentWidth: 16
-ConstructorInitializerIndentWidth: 16
+ContinuationIndentWidth: 8
+ConstructorInitializerIndentWidth: 8
 BreakConstructorInitializers: AfterColon
 AlwaysBreakTemplateDeclarations: Yes
diff --git a/android/app/src/main/java/net/minetest/minetest/CopyZipTask.java b/android/app/src/main/java/net/minetest/minetest/CopyZipTask.java
deleted file mode 100644 (file)
index 6d4b6ab..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
-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);
-               }
-       }
-}
index bdf7641383cd1f2a4086bce20b8747120bc37bb9..46fc9b1de27a55feaba9ca0676dfb02d575d110b 100644 (file)
@@ -171,4 +171,12 @@ public class GameActivity extends NativeActivity {
                Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
                startActivity(browserIntent);
        }
+
+       public String getUserDataPath() {
+               return Utils.getUserDataDirectory(this).getAbsolutePath();
+       }
+
+       public String getCachePath() {
+               return Utils.getCacheDirectory(this).getAbsolutePath();
+       }
 }
index 2aa50d9ad5d2926dd13ea6969dc06b20dcd82c8c..56615fca7664987db941655516ed312222511174 100644 (file)
@@ -29,12 +29,14 @@ import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Environment;
 import android.view.View;
 import android.widget.ProgressBar;
 import android.widget.TextView;
 import android.widget.Toast;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.StringRes;
 import androidx.appcompat.app.AppCompatActivity;
 import androidx.core.app.ActivityCompat;
 import androidx.core.content.ContextCompat;
@@ -43,11 +45,7 @@ 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;
+import static net.minetest.minetest.UnzipService.*;
 
 public class MainActivity extends AppCompatActivity {
        private final static int versionCode = BuildConfig.VERSION_CODE;
@@ -56,26 +54,40 @@ public class MainActivity extends AppCompatActivity {
                        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)
+                       @StringRes int message = 0;
+                       if (intent != null) {
                                progress = intent.getIntExtra(ACTION_PROGRESS, 0);
-                       if (progress >= 0) {
+                               message = intent.getIntExtra(ACTION_PROGRESS_MESSAGE, 0);
+                       }
+
+                       if (progress == FAILURE) {
+                               Toast.makeText(MainActivity.this, intent.getStringExtra(ACTION_FAILURE), Toast.LENGTH_LONG).show();
+                               finish();
+                       } else if (progress == SUCCESS) {
+                               startNative();
+                       } else {
                                if (mProgressBar != null) {
                                        mProgressBar.setVisibility(View.VISIBLE);
-                                       mProgressBar.setProgress(progress);
+                                       if (progress == INDETERMINATE) {
+                                               mProgressBar.setIndeterminate(true);
+                                       } else {
+                                               mProgressBar.setIndeterminate(false);
+                                               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();
+                               if (message != 0)
+                                       mTextView.setText(message);
+                       }
                }
        };
 
@@ -88,6 +100,7 @@ public class MainActivity extends AppCompatActivity {
                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
@@ -120,6 +133,7 @@ public class MainActivity extends AppCompatActivity {
                                if (grantResult != PackageManager.PERMISSION_GRANTED) {
                                        Toast.makeText(this, R.string.not_granted, Toast.LENGTH_LONG).show();
                                        finish();
+                                       return;
                                }
                        }
                        checkAppVersion();
@@ -127,10 +141,27 @@ public class MainActivity extends AppCompatActivity {
        }
 
        private void checkAppVersion() {
-               if (sharedPreferences.getInt(TAG_VERSION_CODE, 0) == versionCode)
+               if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+                       Toast.makeText(this, R.string.no_external_storage, Toast.LENGTH_LONG).show();
+                       finish();
+                       return;
+               }
+
+               if (UnzipService.getIsRunning()) {
+                       mProgressBar.setVisibility(View.VISIBLE);
+                       mProgressBar.setIndeterminate(true);
+                       mTextView.setVisibility(View.VISIBLE);
+               } else if (sharedPreferences.getInt(TAG_VERSION_CODE, 0) == versionCode &&
+                               Utils.isInstallValid(this)) {
                        startNative();
-               else
-                       new CopyZipTask(this).execute(getCacheDir() + "/Minetest.zip");
+               } else {
+                       mProgressBar.setVisibility(View.VISIBLE);
+                       mProgressBar.setIndeterminate(true);
+                       mTextView.setVisibility(View.VISIBLE);
+
+                       Intent intent = new Intent(this, UnzipService.class);
+                       startService(intent);
+               }
        }
 
        private void startNative() {
index b69f7f36e6d34f512fd7fede537e0b2518474a49..b513a7fe0090f2113c5a5c15e0914fd4543c92d8 100644 (file)
@@ -24,16 +24,21 @@ import android.app.IntentService;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Build;
 import android.os.Environment;
-import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
 
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
@@ -42,32 +47,61 @@ 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_PROGRESS_MESSAGE = "net.minetest.minetest.PROGRESS_MESSAGE";
        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;
+       public static final int INDETERMINATE = -3;
        private final int id = 1;
        private NotificationManager mNotifyManager;
        private boolean isSuccess = true;
        private String failureMessage;
 
-       public UnzipService() {
-               super("net.minetest.minetest.UnzipService");
+       private static boolean isRunning = false;
+       public static synchronized boolean getIsRunning() {
+               return isRunning;
+       }
+       private static synchronized void setIsRunning(boolean v) {
+               isRunning = v;
        }
 
-       private void isDir(String dir, String location) {
-               File f = new File(location, dir);
-               if (!f.isDirectory())
-                       f.mkdirs();
+       public UnzipService() {
+               super("net.minetest.minetest.UnzipService");
        }
 
        @Override
        protected void onHandleIntent(Intent intent) {
-               createNotification();
-               unzip(intent);
+               Notification.Builder notificationBuilder = createNotification();
+               final File zipFile = new File(getCacheDir(), "Minetest.zip");
+               try {
+                       setIsRunning(true);
+                       File userDataDirectory = Utils.getUserDataDirectory(this);
+                       if (userDataDirectory == null) {
+                               throw new IOException("Unable to find user data directory");
+                       }
+
+                       try (InputStream in = this.getAssets().open(zipFile.getName())) {
+                               try (OutputStream out = new FileOutputStream(zipFile)) {
+                                       int readLen;
+                                       byte[] readBuffer = new byte[16384];
+                                       while ((readLen = in.read(readBuffer)) != -1) {
+                                               out.write(readBuffer, 0, readLen);
+                                       }
+                               }
+                       }
+
+                       migrate(notificationBuilder, userDataDirectory);
+                       unzip(notificationBuilder, zipFile, userDataDirectory);
+               } catch (IOException e) {
+                       isSuccess = false;
+                       failureMessage = e.getLocalizedMessage();
+               } finally {
+                       setIsRunning(false);
+                       zipFile.delete();
+               }
        }
 
-       private void createNotification() {
+       private Notification.Builder createNotification() {
                String name = "net.minetest.minetest";
                String channelId = "Minetest channel";
                String description = "notifications from Minetest";
@@ -92,66 +126,129 @@ public class UnzipService extends IntentService {
                } else {
                        builder = new Notification.Builder(this);
                }
+
+               Intent notificationIntent = new Intent(this, MainActivity.class);
+               notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
+                       | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+               PendingIntent intent = PendingIntent.getActivity(this, 0,
+                       notificationIntent, 0);
+
                builder.setContentTitle(getString(R.string.notification_title))
                                .setSmallIcon(R.mipmap.ic_launcher)
-                               .setContentText(getString(R.string.notification_description));
+                               .setContentText(getString(R.string.notification_description))
+                               .setContentIntent(intent)
+                               .setOngoing(true)
+                               .setProgress(0, 0, true);
+
                mNotifyManager.notify(id, builder.build());
+               return builder;
        }
 
-       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;
+       private void unzip(Notification.Builder notificationBuilder, File zipFile, File userDataDirectory) throws IOException {
                int per = 0;
-               int size = getSummarySize(zip);
-               File zipFile = new File(zip);
+
+               int size;
+               try (ZipFile zipSize = new ZipFile(zipFile)) {
+                       size = zipSize.size();
+               }
+
                int readLen;
-               byte[] readBuffer = new byte[8192];
+               byte[] readBuffer = new byte[16384];
                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);
-                                               }
+                                       Utils.createDirs(userDataDirectory, ze.getName());
+                                       continue;
+                               }
+                               publishProgress(notificationBuilder, R.string.loading, 100 * ++per / size);
+                               try (OutputStream outputStream = new FileOutputStream(
+                                               new File(userDataDirectory, 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) {
+       void moveFileOrDir(@NonNull File src, @NonNull File dst) throws IOException {
+               try {
+                       Process p = new ProcessBuilder("/system/bin/mv",
+                               src.getAbsolutePath(), dst.getAbsolutePath()).start();
+                       int exitcode = p.waitFor();
+                       if (exitcode != 0)
+                               throw new IOException("Move failed with exit code " + exitcode);
+               } catch (InterruptedException e) {
+                       throw new IOException("Move operation interrupted");
+               }
+       }
+
+       boolean recursivelyDeleteDirectory(@NonNull File loc) {
+               try {
+                       Process p = new ProcessBuilder("/system/bin/rm", "-rf",
+                               loc.getAbsolutePath()).start();
+                       return p.waitFor() == 0;
+               } catch (IOException | InterruptedException e) {
+                       return false;
+               }
+       }
+
+       /**
+        * Migrates user data from deprecated external storage to app scoped storage
+        */
+       private void migrate(Notification.Builder notificationBuilder, File newLocation) throws IOException {
+               File oldLocation = new File(Environment.getExternalStorageDirectory(), "Minetest");
+               if (!oldLocation.isDirectory())
+                       return;
+
+               publishProgress(notificationBuilder, R.string.migrating, 0);
+               newLocation.mkdir();
+
+               String[] dirs = new String[] { "worlds", "games", "mods", "textures", "client" };
+               for (int i = 0; i < dirs.length; i++) {
+                       publishProgress(notificationBuilder, R.string.migrating, 100 * i / dirs.length);
+                       File dir = new File(oldLocation, dirs[i]), dir2 = new File(newLocation, dirs[i]);
+                       if (dir.isDirectory() && !dir2.isDirectory()) {
+                               moveFileOrDir(dir, dir2);
+                       }
+               }
+
+               for (String filename : new String[] { "minetest.conf" }) {
+                       File file = new File(oldLocation, filename), file2 = new File(newLocation, filename);
+                       if (file.isFile() && !file2.isFile()) {
+                               moveFileOrDir(file, file2);
+                       }
+               }
+
+               recursivelyDeleteDirectory(oldLocation);
+       }
+
+       private void publishProgress(@Nullable  Notification.Builder notificationBuilder, @StringRes int message, int progress) {
                Intent intentUpdate = new Intent(ACTION_UPDATE);
                intentUpdate.putExtra(ACTION_PROGRESS, progress);
-               if (!isSuccess) intentUpdate.putExtra(ACTION_FAILURE, failureMessage);
+               intentUpdate.putExtra(ACTION_PROGRESS_MESSAGE, message);
+               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();
+               if (notificationBuilder != null) {
+                       notificationBuilder.setContentText(getString(message));
+                       if (progress == INDETERMINATE) {
+                               notificationBuilder.setProgress(100, 50, true);
+                       } else {
+                               notificationBuilder.setProgress(100, progress, false);
+                       }
+                       mNotifyManager.notify(id, notificationBuilder.build());
                }
-               return size;
        }
 
        @Override
        public void onDestroy() {
                super.onDestroy();
                mNotifyManager.cancel(id);
-               publishProgress(isSuccess ? SUCCESS : FAILURE);
+               publishProgress(null, R.string.loading, isSuccess ? SUCCESS : FAILURE);
        }
 }
diff --git a/android/app/src/main/java/net/minetest/minetest/Utils.java b/android/app/src/main/java/net/minetest/minetest/Utils.java
new file mode 100644 (file)
index 0000000..b2553c8
--- /dev/null
@@ -0,0 +1,39 @@
+package net.minetest.minetest;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import java.io.File;
+
+public class Utils {
+       public static @NonNull File createDirs(File root, String dir) {
+               File f = new File(root, dir);
+               if (!f.isDirectory())
+                       f.mkdirs();
+
+               return f;
+       }
+
+       public static @Nullable File getUserDataDirectory(Context context) {
+               File extDir = context.getExternalFilesDir(null);
+               if (extDir == null) {
+                       return null;
+               }
+
+               return createDirs(extDir, "Minetest");
+       }
+
+       public static @Nullable File getCacheDirectory(Context context) {
+               return context.getCacheDir();
+       }
+
+       public static boolean isInstallValid(Context context) {
+               File userDataDirectory = getUserDataDirectory(context);
+               return userDataDirectory != null && userDataDirectory.isDirectory() &&
+                       new File(userDataDirectory, "games").isDirectory() &&
+                       new File(userDataDirectory, "builtin").isDirectory() &&
+                       new File(userDataDirectory, "client").isDirectory() &&
+                       new File(userDataDirectory, "textures").isDirectory();
+       }
+}
index e6f461f1415f279923f233163479f6a27bb0cdf5..93508c3cb9cf1edf2226704a28f4c1273218b0c5 100644 (file)
@@ -1,4 +1,5 @@
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+       xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
@@ -14,7 +15,8 @@
                android:layout_marginRight="90dp"
                android:indeterminate="false"
                android:max="100"
-               android:visibility="gone" />
+               android:visibility="gone"
+               tools:visibility="visible" />
 
        <TextView
                android:id="@+id/textView"
@@ -25,6 +27,7 @@
                android:background="@android:color/transparent"
                android:text="@string/loading"
                android:textColor="#FEFEFE"
-               android:visibility="gone" />
+               android:visibility="gone"
+               tools:visibility="visible" />
 
 </RelativeLayout>
index 85238117f70ebf7b62e8d6d7ea7a6f7b894136db..99f948c99a2e6a8809b91187aa57d9c24da82c45 100644 (file)
@@ -3,9 +3,11 @@
 
        <string name="label">Minetest</string>
        <string name="loading">Loading&#8230;</string>
+       <string name="migrating">Migrating save data from old install&#8230; (this may take a while)</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&#8230;</string>
        <string name="ime_dialog_done">Done</string>
+       <string name="no_external_storage">External storage isn\'t available. If you use an SDCard, please reinsert it. Otherwise, try restarting your phone or contacting the Minetest developers</string>
 
 </resources>
index 8ea6347b35a9c84df66c7a85a78e73c79acf3c3b..a7f09564138b24bc251e7cb9023ef23611f5b138 100644 (file)
@@ -41,6 +41,10 @@ android {
                                        arguments 'NDEBUG=1'
                                }
                        }
+
+                       ndk {
+                               debugSymbolLevel 'SYMBOL_TABLE'
+                       }
                }
        }
 }
index 29e95b8ca984e5d5a76681110d54c74c70f81262..c71fe5ad88e1c733de7a9a7afceee4d7163139db 100644 (file)
@@ -152,48 +152,35 @@ static std::string javaStringToUTF8(jstring js)
        return str;
 }
 
-// Calls static method if obj is NULL
-static std::string getAndroidPath(
-               jclass cls, jobject obj, jmethodID mt_getAbsPath, const char *getter)
-{
-       // Get getter method
-       jmethodID mt_getter;
-       if (obj)
-               mt_getter = jnienv->GetMethodID(cls, getter, "()Ljava/io/File;");
-       else
-               mt_getter = jnienv->GetStaticMethodID(cls, getter, "()Ljava/io/File;");
-
-       // Call getter
-       jobject ob_file;
-       if (obj)
-               ob_file = jnienv->CallObjectMethod(obj, mt_getter);
-       else
-               ob_file = jnienv->CallStaticObjectMethod(cls, mt_getter);
-
-       // Call getAbsolutePath
-       auto js_path = (jstring) jnienv->CallObjectMethod(ob_file, mt_getAbsPath);
-
-       return javaStringToUTF8(js_path);
-}
-
 void initializePathsAndroid()
 {
-       // Get Environment class
-       jclass cls_Env = jnienv->FindClass("android/os/Environment");
-       // Get File class
-       jclass cls_File = jnienv->FindClass("java/io/File");
-       // Get getAbsolutePath method
-       jmethodID mt_getAbsPath = jnienv->GetMethodID(cls_File,
-                               "getAbsolutePath", "()Ljava/lang/String;");
-       std::string path_storage = getAndroidPath(cls_Env, nullptr,
-                               mt_getAbsPath, "getExternalStorageDirectory");
-
-       path_user    = path_storage + DIR_DELIM + PROJECT_NAME_C;
-       path_share   = path_storage + DIR_DELIM + PROJECT_NAME_C;
-       path_locale  = path_share + DIR_DELIM + "locale";
-       path_cache   = getAndroidPath(nativeActivity,
-                       app_global->activity->clazz, mt_getAbsPath, "getCacheDir");
-       migrateCachePath();
+       // Set user and share paths
+       {
+               jmethodID getUserDataPath = jnienv->GetMethodID(nativeActivity,
+                               "getUserDataPath", "()Ljava/lang/String;");
+               FATAL_ERROR_IF(getUserDataPath==nullptr,
+                               "porting::initializePathsAndroid unable to find Java getUserDataPath method");
+               jobject result = jnienv->CallObjectMethod(app_global->activity->clazz, getUserDataPath);
+               const char *javachars = jnienv->GetStringUTFChars((jstring) result, nullptr);
+               path_user = javachars;
+               path_share = javachars;
+               path_locale  = path_share + DIR_DELIM + "locale";
+               jnienv->ReleaseStringUTFChars((jstring) result, javachars);
+       }
+
+       // Set cache path
+       {
+               jmethodID getCachePath = jnienv->GetMethodID(nativeActivity,
+                               "getCachePath", "()Ljava/lang/String;");
+               FATAL_ERROR_IF(getCachePath==nullptr,
+                               "porting::initializePathsAndroid unable to find Java getCachePath method");
+               jobject result = jnienv->CallObjectMethod(app_global->activity->clazz, getCachePath);
+               const char *javachars = jnienv->GetStringUTFChars((jstring) result, nullptr);
+               path_cache = javachars;
+               jnienv->ReleaseStringUTFChars((jstring) result, javachars);
+
+               migrateCachePath();
+       }
 }
 
 void showInputDialog(const std::string &acceptButton, const std::string &hint,