From b582399d061b318b0c74aaa3c3d547a0c9d52d6d Mon Sep 17 00:00:00 2001 From: octospacc Date: Tue, 5 Nov 2024 01:29:44 +0100 Subject: [PATCH] [SpaccWebView Android] Initial external storage support, JS alerts working, other stuff --- SpaccDotWeb.Android/app/build.gradle | 1 + .../app/src/main/AndroidManifest.xml | 11 ++- .../app/src/main/assets/index.html | 31 +++++++- .../spaccwebviewapplication/MainActivity.java | 18 +---- .../MainApplication.java | 8 ++ .../spacc/spaccdotweb/android/ApiUtils.java | 2 +- .../eu/spacc/spaccdotweb/android/Config.java | 2 +- .../spacc/spaccdotweb/android/Constants.java | 5 +- .../spaccdotweb/android/DataMoveHelper.java | 58 ++++++++++++++ .../spacc/spaccdotweb/android/FileUtils.java | 63 +++++++++++++++ .../spaccdotweb/android/SharedPrefHelper.java | 27 +++++++ .../android/SpaccWebChromeClient.java | 33 ++++++++ .../spaccdotweb/android/SpaccWebView.java | 12 +-- .../android/SpaccWebViewActivity.java | 44 +++++++++++ .../android/SpaccWebViewApplication.java | 35 +++++++++ .../android/SpaccWebViewClient.java | 37 +++++++++ .../spaccdotweb/android/StorageUtils.java | 78 +++++++++++++++++++ .../app/src/main/res/values-it/strings.xml | 6 ++ .../app/src/main/res/values/strings.xml | 6 +- 19 files changed, 447 insertions(+), 30 deletions(-) create mode 100644 SpaccDotWeb.Android/app/src/main/java/com/example/spaccwebviewapplication/MainApplication.java create mode 100644 SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/DataMoveHelper.java create mode 100644 SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/FileUtils.java create mode 100644 SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/SharedPrefHelper.java create mode 100644 SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/SpaccWebChromeClient.java create mode 100644 SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/SpaccWebViewActivity.java create mode 100644 SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/SpaccWebViewApplication.java create mode 100644 SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/SpaccWebViewClient.java create mode 100644 SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/StorageUtils.java create mode 100644 SpaccDotWeb.Android/app/src/main/res/values-it/strings.xml diff --git a/SpaccDotWeb.Android/app/build.gradle b/SpaccDotWeb.Android/app/build.gradle index 93de6ca..9d57da6 100644 --- a/SpaccDotWeb.Android/app/build.gradle +++ b/SpaccDotWeb.Android/app/build.gradle @@ -20,6 +20,7 @@ android { } release { minifyEnabled true + shrinkResources true signingConfig signingConfigs.debug } } diff --git a/SpaccDotWeb.Android/app/src/main/AndroidManifest.xml b/SpaccDotWeb.Android/app/src/main/AndroidManifest.xml index 87ab13b..b822565 100644 --- a/SpaccDotWeb.Android/app/src/main/AndroidManifest.xml +++ b/SpaccDotWeb.Android/app/src/main/AndroidManifest.xml @@ -7,18 +7,23 @@ - - + + + + - \ No newline at end of file + diff --git a/SpaccDotWeb.Android/app/src/main/assets/index.html b/SpaccDotWeb.Android/app/src/main/assets/index.html index 0f0fbd3..6b6cfa9 100644 --- a/SpaccDotWeb.Android/app/src/main/assets/index.html +++ b/SpaccDotWeb.Android/app/src/main/assets/index.html @@ -1,3 +1,30 @@ -

SpaccWebView Application

-

https://gitlab.com/SpaccInc/SpaccDotWeb

+ + +

SpaccWebView Example Android Application

+

Repository: https://gitlab.com/SpaccInc/SpaccDotWeb.

+

Tests

+

JavaScript

+

+ + +

+

Popups

+

+ + + +

+

Links

+ +

Files

+

Upload:

+

Download: Download

+ + \ No newline at end of file diff --git a/SpaccDotWeb.Android/app/src/main/java/com/example/spaccwebviewapplication/MainActivity.java b/SpaccDotWeb.Android/app/src/main/java/com/example/spaccwebviewapplication/MainActivity.java index 02c11d5..a763913 100644 --- a/SpaccDotWeb.Android/app/src/main/java/com/example/spaccwebviewapplication/MainActivity.java +++ b/SpaccDotWeb.Android/app/src/main/java/com/example/spaccwebviewapplication/MainActivity.java @@ -1,28 +1,18 @@ package com.example.spaccwebviewapplication; -import android.app.Activity; import android.os.Bundle; import org.eu.spacc.spaccdotweb.android.*; -public class MainActivity extends Activity { - private SpaccWebView webView; +public class MainActivity extends SpaccWebViewActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - webView = findViewById(R.id.webview); - webView.loadAppIndex(); - } + DataMoveHelper.run(this, R.string.exit, R.string.move_app_data, R.string.move_app_data_info); - @Override - public void onBackPressed() { - if (webView.canGoBack()) { - webView.goBack(); - } else { - super.onBackPressed(); - } + this.webView = findViewById(R.id.webview); + this.webView.loadAppIndex(); } } diff --git a/SpaccDotWeb.Android/app/src/main/java/com/example/spaccwebviewapplication/MainApplication.java b/SpaccDotWeb.Android/app/src/main/java/com/example/spaccwebviewapplication/MainApplication.java new file mode 100644 index 0000000..de00044 --- /dev/null +++ b/SpaccDotWeb.Android/app/src/main/java/com/example/spaccwebviewapplication/MainApplication.java @@ -0,0 +1,8 @@ +package com.example.spaccwebviewapplication; + +import org.eu.spacc.spaccdotweb.android.SpaccWebViewApplication; + +public class MainApplication extends SpaccWebViewApplication { + // This proxy class can be left empty, + // it exists just to provide an unique android:name for the manifest +} diff --git a/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/ApiUtils.java b/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/ApiUtils.java index 3a092fd..2419569 100644 --- a/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/ApiUtils.java +++ b/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/ApiUtils.java @@ -9,4 +9,4 @@ public class ApiUtils { action.run(); } } -} \ No newline at end of file +} diff --git a/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/Config.java b/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/Config.java index 1a57706..6471e27 100644 --- a/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/Config.java +++ b/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/Config.java @@ -8,4 +8,4 @@ public class Config { public static final AppIndex APP_INDEX = AppIndex.LOCAL; public static final String LOCAL_INDEX = "index.html"; public static final String REMOTE_INDEX = "https://example.com"; -} \ No newline at end of file +} diff --git a/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/Constants.java b/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/Constants.java index be39f19..f4336d4 100644 --- a/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/Constants.java +++ b/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/Constants.java @@ -1,5 +1,6 @@ package org.eu.spacc.spaccdotweb.android; public class Constants { - public static enum AppIndex { LOCAL, REMOTE } -} \ No newline at end of file + public enum AppIndex { LOCAL, REMOTE } + public enum DataLocation { INTERNAL, EXTERNAL } +} diff --git a/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/DataMoveHelper.java b/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/DataMoveHelper.java new file mode 100644 index 0000000..c565bc1 --- /dev/null +++ b/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/DataMoveHelper.java @@ -0,0 +1,58 @@ +package org.eu.spacc.spaccdotweb.android; + +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 java.io.IOException; + +public class DataMoveHelper { + + public static void run(Context context, int labelExit, int dialogTitle, int dialogMessage) { + Activity activity = (Activity)context; + SharedPrefHelper sharedPrefHelper = new SharedPrefHelper(context); + Constants.DataLocation dataLocationReal = (StorageUtils.isInstalledOnExternalStorage(context) ? Constants.DataLocation.EXTERNAL : Constants.DataLocation.INTERNAL); + Integer dataLocationSaved = sharedPrefHelper.getInt("data_location"); + if (dataLocationSaved == null) { + sharedPrefHelper.setInt("data_location", dataLocationReal.ordinal()); + } else if (!dataLocationSaved.equals(dataLocationReal.ordinal())) { + new AlertDialog.Builder(context) + .setTitle(dialogTitle) + .setMessage(dialogMessage) + .setCancelable(false) + .setNegativeButton(labelExit, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + ((Activity)context).finish(); + } + }) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + // TODO: Check that the storage locations are all present to copy, and implement an error dialog + try { + FileUtils.moveDirectory(StorageUtils.dataDirFromEnum(context, Constants.DataLocation.values()[dataLocationSaved]), StorageUtils.dataDirFromEnum(context, dataLocationReal), false); + } catch (IOException e) { + throw new RuntimeException(e); + } + sharedPrefHelper.setInt("data_location", dataLocationReal.ordinal()); + restartActivity(context); + } + }) + .show(); + } + } + + private static void restartActivity(Context context) { + Activity activity = (Activity)context; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + activity.recreate(); + } else { + Intent intent = activity.getIntent(); + activity.finish(); + context.startActivity(intent); + } + } +} diff --git a/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/FileUtils.java b/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/FileUtils.java new file mode 100644 index 0000000..f90b4c6 --- /dev/null +++ b/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/FileUtils.java @@ -0,0 +1,63 @@ +package org.eu.spacc.spaccdotweb.android; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class FileUtils { + + public static void moveDirectory(File sourceLocation, File targetLocation, boolean deleteRoot) throws IOException { + copyDirectory(sourceLocation, targetLocation); + recursiveDelete(sourceLocation, deleteRoot); + } + + /* https://subversivebytes.wordpress.com/2012/11/05/java-copy-directory-recursive-delete/ */ + + public static void copyDirectory(File sourceLocation, File targetLocation) throws IOException { + if (sourceLocation.isDirectory()) { + if (!targetLocation.exists()) { + targetLocation.mkdir(); + } + String[] children = sourceLocation.list(); + for (int i = 0; i < children.length; i++) { + copyDirectory(new File(sourceLocation, children[i]), new File(targetLocation, children[i])); + } + } + else { + InputStream in = new FileInputStream(sourceLocation); + OutputStream out = new FileOutputStream(targetLocation); + try { + byte[] buf = new byte[1024]; + int len; + while((len = in.read(buf)) > 0) { + out.write(buf, 0, len); + } + } finally { + in.close(); + out.close(); + } + } + } + + public void recursiveDelete(File rootDir) { + recursiveDelete(rootDir, true); + } + + public static void recursiveDelete(File rootDir, boolean deleteRoot) { + File[] childDirs = rootDir.listFiles(); + for (File childDir : childDirs) { + if (childDir.isFile()) { + childDir.delete(); + } else { + recursiveDelete(childDir, deleteRoot); + childDir.delete(); + } + } + if (deleteRoot) { + rootDir.delete(); + } + } +} diff --git a/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/SharedPrefHelper.java b/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/SharedPrefHelper.java new file mode 100644 index 0000000..0f912fa --- /dev/null +++ b/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/SharedPrefHelper.java @@ -0,0 +1,27 @@ +package org.eu.spacc.spaccdotweb.android; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Build; + +public class SharedPrefHelper { + private final SharedPreferences sharedPref; + + public SharedPrefHelper(Context context) { + this.sharedPref = context.getSharedPreferences("SpaccWebView", Context.MODE_PRIVATE); + } + + public Integer getInt(String name) { + Integer value = (Integer)sharedPref.getInt(name, -1); + return (value != -1 ? value : null); + } + + public void setInt(String name, int value) { + SharedPreferences.Editor editor = sharedPref.edit().putInt(name, value); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { + editor.apply(); + } else { + editor.commit(); + } + } +} diff --git a/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/SpaccWebChromeClient.java b/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/SpaccWebChromeClient.java new file mode 100644 index 0000000..34a949b --- /dev/null +++ b/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/SpaccWebChromeClient.java @@ -0,0 +1,33 @@ +package org.eu.spacc.spaccdotweb.android; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.webkit.ValueCallback; +import android.webkit.WebChromeClient; +import android.webkit.WebView; + +public class SpaccWebChromeClient extends WebChromeClient { + private static final int INPUT_FILE_REQUEST_CODE = 1; + + private final Context context; + + public SpaccWebChromeClient(Context context) { + super(); + this.context = context; + } + + // TODO: This only opens a file selector but then doesn't do anything + // TODO: Android < 5 support + @Override + public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, FileChooserParams fileChooserParams) { + Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT); + contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE); + contentSelectionIntent.setType("*/*"); // TODO: Read type from HTML input + Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER); + chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent); + ((Activity)context).startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_CODE); + return true; + } +} diff --git a/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/SpaccWebView.java b/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/SpaccWebView.java index 44ec7fe..e5c0eb2 100644 --- a/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/SpaccWebView.java +++ b/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/SpaccWebView.java @@ -11,23 +11,24 @@ public class SpaccWebView extends WebView { @SuppressLint("SetJavaScriptEnabled") public SpaccWebView(Context context, AttributeSet attrs) { super(context, attrs); + this.setWebViewClient(new SpaccWebViewClient(context)); + this.setWebChromeClient(new SpaccWebChromeClient(context)); - WebSettings webSettings = getSettings(); + WebSettings webSettings = this.getSettings(); webSettings.setJavaScriptEnabled(Config.ALLOW_JAVASCRIPT); ApiUtils.apiRun(7, () -> webSettings.setDomStorageEnabled(Config.ALLOW_STORAGE)); ApiUtils.apiRun(5, () -> webSettings.setDatabaseEnabled(Config.ALLOW_STORAGE)); if (Config.ALLOW_STORAGE) { - ApiUtils.apiRun(5, () -> webSettings.setDatabasePath(context.getFilesDir().getParent() + "/databases")); + ApiUtils.apiRun(5, () -> webSettings.setDatabasePath(context.getDir("databases", 0).getAbsolutePath())); } - ApiUtils.apiRun(3, () -> webSettings.setAllowFileAccess(true)); + ApiUtils.apiRun(3, () -> webSettings.setAllowFileAccess(false)); } public void loadAppIndex() { String url = null; - switch (Config.APP_INDEX) { case LOCAL: url = ("file:///android_asset/" + Config.LOCAL_INDEX); @@ -36,7 +37,6 @@ public class SpaccWebView extends WebView { url = Config.REMOTE_INDEX; break; } - loadUrl(url); } -} \ No newline at end of file +} diff --git a/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/SpaccWebViewActivity.java b/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/SpaccWebViewActivity.java new file mode 100644 index 0000000..6096540 --- /dev/null +++ b/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/SpaccWebViewActivity.java @@ -0,0 +1,44 @@ +package org.eu.spacc.spaccdotweb.android; + +import android.annotation.SuppressLint; +import android.app.Activity; +import java.io.File; + +public class SpaccWebViewActivity extends Activity { + protected SpaccWebView webView; + + @Override + public void onBackPressed() { + if (this.webView.canGoBack()) { + this.webView.goBack(); + } else { + super.onBackPressed(); + } + } + + @SuppressLint("NewApi") // We have our custom implementation + @Override + public File getDataDir() { + return getApplicationContext().getDataDir(); + } + + @Override + public File getDir(String name, int mode) { + return getApplicationContext().getDir(name, mode); + } + + @Override + public File getFilesDir() { + return getApplicationContext().getFilesDir(); + } + + @Override + public File getCacheDir() { + return getApplicationContext().getCacheDir(); + } + + @Override + public File getDatabasePath(String name) { + return getApplicationContext().getDatabasePath(name); + } +} diff --git a/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/SpaccWebViewApplication.java b/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/SpaccWebViewApplication.java new file mode 100644 index 0000000..eb2463f --- /dev/null +++ b/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/SpaccWebViewApplication.java @@ -0,0 +1,35 @@ +package org.eu.spacc.spaccdotweb.android; + +import android.app.Application; +import java.io.File; + +public class SpaccWebViewApplication extends Application { + + @Override + public File getDataDir() { + return StorageUtils.getDataDir(this); + } + + @Override + public File getDir(String name, int mode) { + File dir = new File(getDataDir(), name); + dir.mkdirs(); + return dir; + } + + @Override + public File getFilesDir() { + return getDir("files", 0); + } + + @Override + public File getCacheDir() { + return getDir("cache", 0); + } + + @Override + public File getDatabasePath(String name) { + // TODO: should this be "app_databases"? + return new File(getDir("databases", 0), name); + } +} diff --git a/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/SpaccWebViewClient.java b/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/SpaccWebViewClient.java new file mode 100644 index 0000000..a962a18 --- /dev/null +++ b/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/SpaccWebViewClient.java @@ -0,0 +1,37 @@ +package org.eu.spacc.spaccdotweb.android; + +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import java.util.Arrays; +import java.util.List; + +public class SpaccWebViewClient extends WebViewClient { + private final Context context; + + public SpaccWebViewClient(Context context) { + super(); + this.context = context; + } + + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + // TODO: This should not override all HTTP links if the app loads from remote + List protocols = Arrays.asList("http", "https", "mailto", "ftp"); + if (protocols.contains(url.toLowerCase().split(":")[0])) { + try { + // Open the link externally + context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); + } catch (ActivityNotFoundException ignored) { + // No app can handle it, so share it instead + context.startActivity(new Intent(Intent.ACTION_SEND).setType("text/plain").putExtra(Intent.EXTRA_TEXT, url)); + } + return true; + } else { + return super.shouldOverrideUrlLoading(view, url); + } + } +} diff --git a/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/StorageUtils.java b/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/StorageUtils.java new file mode 100644 index 0000000..939cf34 --- /dev/null +++ b/SpaccDotWeb.Android/app/src/main/java/org/eu/spacc/spaccdotweb/android/StorageUtils.java @@ -0,0 +1,78 @@ +package org.eu.spacc.spaccdotweb.android; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.Environment; +import java.io.File; +import org.eu.spacc.spaccdotweb.android.Constants.*; + +public class StorageUtils { + + public static boolean isInstalledOnExternalStorage(Context context) { + try { + int flags = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).applicationInfo.flags; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { + // https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/content/pm/ApplicationInfo.java#2516 + return ((flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0); + } + } catch (PackageManager.NameNotFoundException ignored) {} + return false; + } + + public static File getProtectedDataDir(Context context) { + // Usually is /data/data/ + return new File(Environment.getDataDirectory() + File.separator + "data" + File.separator + context.getPackageName()); + } + + public static File getInternalDataDir(Context context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { + // Usually is /sdcard/Android/data/ + return getParentDir(context.getExternalFilesDir(null)); + } else { + // TODO: This can actually be external storage on old Androids, we should make it return null in those cases + return new File(Environment.getExternalStorageDirectory() + "Android" + File.separator + "data" + File.separator + context.getPackageName()); + } + } + + public static File getExternalDataDir(Context context) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { + File[] dirs = context.getExternalFilesDirs(null); + if (dirs.length >= 2) { + return getParentDir(dirs[1]); + } else { + return null; + } + } else { + // TODO: We need hacks for old Android systems which have emulated internal + real external storages + return getInternalDataDir(context); + } + } + + // TODO: This should not suggest to use external storage if we don't have the necessary manifest permission + public static File getDataDir(Context context) { + File dir = null; + if (isInstalledOnExternalStorage(context)) { + dir = getExternalDataDir(context); + } + if (dir == null) { + dir = getProtectedDataDir(context); + } + return dir; + } + + public static File dataDirFromEnum(Context context, DataLocation dataLocation) { + switch (dataLocation) { + case INTERNAL: + return getProtectedDataDir(context); + case EXTERNAL: + return getExternalDataDir(context); + } + return null; + } + + private static File getParentDir(File path) { + return (path != null ? path.getParentFile() : null); + } +} diff --git a/SpaccDotWeb.Android/app/src/main/res/values-it/strings.xml b/SpaccDotWeb.Android/app/src/main/res/values-it/strings.xml new file mode 100644 index 0000000..1602d42 --- /dev/null +++ b/SpaccDotWeb.Android/app/src/main/res/values-it/strings.xml @@ -0,0 +1,6 @@ + + + Esci + Sposta Dati App + La app è stata trasferita su una diversa locazione di archiviazione. I dati saranno spostati ora. + \ No newline at end of file diff --git a/SpaccDotWeb.Android/app/src/main/res/values/strings.xml b/SpaccDotWeb.Android/app/src/main/res/values/strings.xml index 52d54c3..e02a8f5 100644 --- a/SpaccDotWeb.Android/app/src/main/res/values/strings.xml +++ b/SpaccDotWeb.Android/app/src/main/res/values/strings.xml @@ -1,3 +1,7 @@ + - SpaccWebView Application + SpaccWebView Application + Exit + Move App Data + The app has been transferred to a different storage location. The data will be moved now. \ No newline at end of file