mirror of
https://gitlab.com/SpaccInc/SpaccDotWeb.git
synced 2025-06-05 21:29:12 +02:00
Update SpaccWebView
This commit is contained in:
2
SpaccDotWeb.Android/.gitignore
vendored
2
SpaccDotWeb.Android/.gitignore
vendored
@ -1,6 +1,7 @@
|
|||||||
*.iml
|
*.iml
|
||||||
.gradle
|
.gradle
|
||||||
/local.properties
|
/local.properties
|
||||||
|
/keystore.properties
|
||||||
.idea
|
.idea
|
||||||
.DS_Store
|
.DS_Store
|
||||||
/build
|
/build
|
||||||
@ -8,3 +9,4 @@
|
|||||||
.externalNativeBuild
|
.externalNativeBuild
|
||||||
.cxx
|
.cxx
|
||||||
local.properties
|
local.properties
|
||||||
|
keystore.properties
|
||||||
|
@ -3,29 +3,45 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
// Adjust the very following based on the application to ship
|
// Adjust namespace and defaultConfig very following based on the application to ship
|
||||||
|
|
||||||
namespace 'com.example.spaccwebviewapplication'
|
namespace 'com.example.spaccwebviewapplication'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId 'com.example.spaccwebviewapplication'
|
applicationId 'com.example.spaccwebviewapplication'
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName '1.0'
|
versionName '1.0'
|
||||||
|
|
||||||
minSdk 1
|
minSdk 1
|
||||||
|
//noinspection OldTargetApi
|
||||||
targetSdk 34
|
targetSdk 34
|
||||||
|
compileSdk 34
|
||||||
|
}
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
release {
|
||||||
|
def Properties localProps = new Properties()
|
||||||
|
def Properties keyProps = new Properties()
|
||||||
|
localProps.load(new FileInputStream(file('../local.properties')))
|
||||||
|
keyProps.load(new FileInputStream(file('../keystore.properties')))
|
||||||
|
storeFile file(localProps['storeFile'])
|
||||||
|
storePassword keyProps['storePassword']
|
||||||
|
keyPassword keyProps['keyPassword']
|
||||||
|
keyAlias keyProps['keyAlias']
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
debug {
|
debug {
|
||||||
applicationIdSuffix '.dbg'
|
applicationIdSuffix '.dbg'
|
||||||
|
versionNameSuffix '.debug'
|
||||||
|
signingConfig signingConfigs.debug
|
||||||
}
|
}
|
||||||
release {
|
release {
|
||||||
minifyEnabled true
|
minifyEnabled true
|
||||||
shrinkResources true
|
shrinkResources true
|
||||||
signingConfig signingConfigs.debug
|
signingConfig signingConfigs.release
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compileSdk 34
|
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
|
@ -6,44 +6,56 @@
|
|||||||
tools:ignore="UnusedAttribute">
|
tools:ignore="UnusedAttribute">
|
||||||
|
|
||||||
<!-- Lets the app access the Internet — not needed for fully offline apps -->
|
<!-- Lets the app access the Internet — not needed for fully offline apps -->
|
||||||
<!-- <uses-permission android:name="android.permission.INTERNET" /> -->
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
<!-- Needed from Android ??? to 4.4 KitKat (API ???-19) to keep app data on external storage -->
|
<!-- Needed from Android ??? to 4.4 KitKat (API ???-19) to keep app data on external storage -->
|
||||||
<!-- Removing these will not break the app, but it will write only on internal storage on those versions -->
|
<!-- Removing these will not break the app, but it will write only on internal storage on those versions -->
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" />
|
||||||
|
|
||||||
<!-- Additional suggested attributes:
|
<!-- Needed for notifications on Android 13+ (API 33+) -->
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
|
||||||
|
<!-- Camera access to take photos and record videos -->
|
||||||
|
<!-- <uses-permission android:name="android.permission.CAMERA" />
|
||||||
|
<uses-feature android:name="android.hardware.camera" android:required="false" /> -->
|
||||||
|
|
||||||
|
<!-- Microphone access to record audio -->
|
||||||
|
<!-- <uses-permission android:name="android.permission.RECORD_AUDIO" /> -->
|
||||||
|
|
||||||
|
<!-- Additional suggested attributes for <application>:
|
||||||
android:appCategory=["accessibility" | "audio" | "game" | "image" | "maps" | "news" | "productivity" | "social" | "video"]
|
android:appCategory=["accessibility" | "audio" | "game" | "image" | "maps" | "news" | "productivity" | "social" | "video"]
|
||||||
android:isGame=["true" | "false"]
|
android:isGame=["true" | "false"]
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:usesCleartextTraffic=["true" | "false"]
|
android:usesCleartextTraffic=["true" | "false"]
|
||||||
|
android:theme="@android:style/Theme.DeviceDefault.NoActionBar" // to disable the ActionBar
|
||||||
|
android:theme="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen" // to put the app in Fullscreen
|
||||||
-->
|
-->
|
||||||
<application
|
<application
|
||||||
android:name=".MainApplication"
|
android:name=".MainApplication"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:theme="@android:style/Theme.DeviceDefault.NoActionBar"
|
|
||||||
android:resizeableActivity="true"
|
android:resizeableActivity="true"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:fullBackupContent="@xml/backup_rules"
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
android:hasFragileUserData="true"
|
android:hasFragileUserData="true"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
android:hasCode="true"
|
android:hasCode="true"
|
||||||
tools:targetApi="34">
|
tools:targetApi="34">
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:configChanges="orientation|keyboardHidden|screenSize|screenLayout|layoutDirection"
|
android:configChanges="density|fontScale|orientation|keyboard|keyboardHidden|screenSize|smallestScreenSize|uiMode|screenLayout|layoutDirection"
|
||||||
android:launchMode="singleTask">
|
android:launchMode="singleTask">
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
<h1>SpaccWebView Example Android Application</h1>
|
<h1>SpaccWebView Example Android Application</h1>
|
||||||
<p>Repository: <a href="https://gitlab.com/SpaccInc/SpaccDotWeb">https://gitlab.com/SpaccInc/SpaccDotWeb</a>.</p>
|
<p>Repository: <a href="https://gitlab.com/SpaccInc/SpaccDotWeb">https://gitlab.com/SpaccInc/SpaccDotWeb</a>.</p>
|
||||||
<h2>Tests</h2>
|
<h2>Tests</h2>
|
||||||
|
<img alt="Cat!" src="https://http.cat/images/200.jpg" />
|
||||||
<h3>JavaScript</h3>
|
<h3>JavaScript</h3>
|
||||||
<p>
|
<p>
|
||||||
<label><input type="text" autocomplete="off" onkeypress="(function(event){if(event.keyCode===13)eval(event.target.value);})(event);"/></label>
|
<label><input type="text" autocomplete="off" onkeypress="(function(event){if(event.keyCode===13)eval(event.target.value);})(event);"/></label>
|
||||||
|
@ -1,20 +1,145 @@
|
|||||||
package com.example.spaccwebviewapplication;
|
package com.example.spaccwebviewapplication;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.app.ActionBar;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.webkit.WebView;
|
||||||
|
import android.widget.EditText;
|
||||||
|
|
||||||
|
import org.eu.spacc.spaccdotweb.android.utils.ApiUtils;
|
||||||
import org.eu.spacc.spaccdotweb.android.helpers.DataMoveHelper;
|
import org.eu.spacc.spaccdotweb.android.helpers.DataMoveHelper;
|
||||||
import org.eu.spacc.spaccdotweb.android.SpaccWebViewActivity;
|
import org.eu.spacc.spaccdotweb.android.SpaccWebViewActivity;
|
||||||
|
import org.eu.spacc.spaccdotweb.android.webview.SpaccWebViewClient;
|
||||||
|
|
||||||
public class MainActivity extends SpaccWebViewActivity {
|
public class MainActivity extends SpaccWebViewActivity {
|
||||||
|
private ActionBar actionBar = null;
|
||||||
|
private Menu menu = null;
|
||||||
|
private long pageStartTime = 0;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
|
ApiUtils.apiRun(11, () -> this.actionBar = getActionBar());
|
||||||
|
|
||||||
DataMoveHelper.run(this, R.string.exit, R.string.move_app_data, R.string.move_app_data_info);
|
DataMoveHelper.run(this, R.string.exit, R.string.move_app_data, R.string.move_app_data_info);
|
||||||
|
|
||||||
this.webView = findViewById(R.id.webview);
|
this.webView = findViewById(R.id.webview);
|
||||||
|
this.webView.setStrings(R.string.open_menu, R.string.open_externally_menu, R.string.copy_url_menu);
|
||||||
|
this.webView.setWebViewClient(new SpaccWebViewClient(this) {
|
||||||
|
@SuppressLint("UseRequiresApi")
|
||||||
|
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||||
|
@Override
|
||||||
|
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||||
|
super.onPageStarted(view, url, favicon);
|
||||||
|
pageStartTime = System.currentTimeMillis();
|
||||||
|
if (menu != null) {
|
||||||
|
menu.findItem(R.id.stop).setVisible(true);
|
||||||
|
menu.findItem(R.id.reload).setVisible(false);
|
||||||
|
menu.findItem(R.id.open_externally).setVisible(!ApiUtils.isInternalUrl(Uri.parse(url)));
|
||||||
|
menu.findItem(R.id.about_app).setVisible(webView.getConfig().getAboutPage() != null);
|
||||||
|
menu.findItem(R.id.backwards).setEnabled(webView.canGoBack());
|
||||||
|
menu.findItem(R.id.forward).setEnabled(webView.canGoForward());
|
||||||
|
}
|
||||||
|
if (actionBar != null) {
|
||||||
|
actionBar.setSubtitle(R.string.loading);
|
||||||
|
}
|
||||||
|
// if (justStarted) {
|
||||||
|
// new Handler().postDelayed(() -> Objects.requireNonNull(getActionBar()).hide(), 3000);
|
||||||
|
// justStarted = false;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("UseRequiresApi")
|
||||||
|
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||||
|
@Override
|
||||||
|
public void onPageFinished(WebView view, String url) {
|
||||||
|
super.onPageFinished(view, url);
|
||||||
|
if (menu != null) {
|
||||||
|
menu.findItem(R.id.stop).setVisible(false);
|
||||||
|
menu.findItem(R.id.reload).setVisible(true);
|
||||||
|
}
|
||||||
|
if (actionBar != null && pageStartTime != 0) {
|
||||||
|
actionBar.setSubtitle("~" + (System.currentTimeMillis() - pageStartTime) + "ms");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
this.webView.loadConfig(this, R.xml.app_config);
|
this.webView.loadConfig(this, R.xml.app_config);
|
||||||
this.webView.loadAppIndex();
|
this.webView.loadAppIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.main, menu);
|
||||||
|
if (actionBar == null) {
|
||||||
|
menu.findItem(R.id.hide).setVisible(false);
|
||||||
|
}
|
||||||
|
return super.onCreateOptionsMenu(this.menu = menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint({"NonConstantResourceId", "UseRequiresApi"})
|
||||||
|
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.stop:
|
||||||
|
this.webView.stopLoading();
|
||||||
|
break;
|
||||||
|
case R.id.reload:
|
||||||
|
this.webView.reload();
|
||||||
|
break;
|
||||||
|
case R.id.backwards:
|
||||||
|
this.webView.goBack();
|
||||||
|
break;
|
||||||
|
case R.id.forward:
|
||||||
|
this.webView.goForward();
|
||||||
|
break;
|
||||||
|
case R.id.copy_url:
|
||||||
|
ApiUtils.writeToClipboard(this, webView.getUrl());
|
||||||
|
break;
|
||||||
|
case R.id.open_externally:
|
||||||
|
ApiUtils.openOrShareUrl(this, Uri.parse(webView.getUrl()));
|
||||||
|
break;
|
||||||
|
case R.id.exec_script:
|
||||||
|
EditText scriptText = new EditText(this);
|
||||||
|
new AlertDialog.Builder(this)
|
||||||
|
.setTitle(R.string.execute_javascript)
|
||||||
|
.setView(scriptText)
|
||||||
|
.setPositiveButton("OK", (dialogInterface, i) -> webView.injectScript(scriptText.getText().toString()))
|
||||||
|
.setNeutralButton(R.string.cancel, null)
|
||||||
|
.show();
|
||||||
|
break;
|
||||||
|
case R.id.hide:
|
||||||
|
actionBar.hide();
|
||||||
|
break;
|
||||||
|
case R.id.exit:
|
||||||
|
finish();
|
||||||
|
break;
|
||||||
|
case R.id.about_app:
|
||||||
|
ApiUtils.openOrShareUrl(this, Uri.parse(webView.getConfig().getAboutPage()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("UseRequiresApi")
|
||||||
|
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
if (actionBar == null) {
|
||||||
|
super.onBackPressed();
|
||||||
|
} else if (actionBar.isShowing()) {
|
||||||
|
actionBar.hide();
|
||||||
|
} else {
|
||||||
|
actionBar.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package org.eu.spacc.spaccdotweb.android;
|
package org.eu.spacc.spaccdotweb.android;
|
||||||
|
|
||||||
import android.webkit.WebSettings;
|
|
||||||
|
|
||||||
import org.eu.spacc.spaccdotweb.android.Constants.*;
|
import org.eu.spacc.spaccdotweb.android.Constants.*;
|
||||||
import org.eu.spacc.spaccdotweb.android.helpers.ConfigReader;
|
import org.eu.spacc.spaccdotweb.android.helpers.ConfigReader;
|
||||||
|
|
||||||
@ -14,6 +12,10 @@ public class Config extends Defaults {
|
|||||||
this.configReader = configReader;
|
this.configReader = configReader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getAboutPage() {
|
||||||
|
return getString("about_page");
|
||||||
|
}
|
||||||
|
|
||||||
public Boolean getAllowJavascript() {
|
public Boolean getAllowJavascript() {
|
||||||
Boolean value = getBoolean("allow_javascript");
|
Boolean value = getBoolean("allow_javascript");
|
||||||
return (value != null ? value : Defaults.ALLOW_JAVASCRIPT);
|
return (value != null ? value : Defaults.ALLOW_JAVASCRIPT);
|
||||||
@ -24,6 +26,26 @@ public class Config extends Defaults {
|
|||||||
return (value != null ? value : Defaults.ALLOW_STORAGE);
|
return (value != null ? value : Defaults.ALLOW_STORAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean getAllowAutoplay() {
|
||||||
|
Boolean value = getBoolean("allow_autoplay");
|
||||||
|
return (value != null ? value : Defaults.ALLOW_AUTOPLAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getAllowDrmMedia() {
|
||||||
|
Boolean value = getBoolean("allow_drm_media");
|
||||||
|
return (value != null ? value : Defaults.ALLOW_DRM_MEDIA);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getAllowAudioCapture() {
|
||||||
|
Boolean value = getBoolean("allow_audio_capture");
|
||||||
|
return (value != null ? value : Defaults.ALLOW_AUDIO_CAPTURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getAllowVideoCapture() {
|
||||||
|
Boolean value = getBoolean("allow_video_capture");
|
||||||
|
return (value != null ? value : Defaults.ALLOW_VIDEO_CAPTURE);
|
||||||
|
}
|
||||||
|
|
||||||
public Boolean getAllowZoomControls() {
|
public Boolean getAllowZoomControls() {
|
||||||
Boolean value = getBoolean("allow_zoom_controls");
|
Boolean value = getBoolean("allow_zoom_controls");
|
||||||
return (value != null ? value : Defaults.ALLOW_ZOOM_CONTROLS);
|
return (value != null ? value : Defaults.ALLOW_ZOOM_CONTROLS);
|
||||||
|
@ -5,6 +5,10 @@ import org.eu.spacc.spaccdotweb.android.Constants.*;
|
|||||||
public class Defaults {
|
public class Defaults {
|
||||||
public static final Boolean ALLOW_JAVASCRIPT = true;
|
public static final Boolean ALLOW_JAVASCRIPT = true;
|
||||||
public static final Boolean ALLOW_STORAGE = true;
|
public static final Boolean ALLOW_STORAGE = true;
|
||||||
|
public static final Boolean ALLOW_AUTOPLAY = true;
|
||||||
|
public static final Boolean ALLOW_DRM_MEDIA = true;
|
||||||
|
public static final Boolean ALLOW_AUDIO_CAPTURE = false;
|
||||||
|
public static final Boolean ALLOW_VIDEO_CAPTURE = false;
|
||||||
public static final Boolean ALLOW_ZOOM_CONTROLS = false;
|
public static final Boolean ALLOW_ZOOM_CONTROLS = false;
|
||||||
public static final Boolean DISPLAY_ZOOM_CONTROLS = false;
|
public static final Boolean DISPLAY_ZOOM_CONTROLS = false;
|
||||||
public static final AppIndex APP_INDEX = AppIndex.LOCAL;
|
public static final AppIndex APP_INDEX = AppIndex.LOCAL;
|
||||||
|
@ -7,9 +7,10 @@ import android.content.Intent;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.webkit.ValueCallback;
|
import android.webkit.ValueCallback;
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
import org.eu.spacc.spaccdotweb.android.webview.SpaccWebChromeClient;
|
import org.eu.spacc.spaccdotweb.android.webview.SpaccWebChromeClient;
|
||||||
import org.eu.spacc.spaccdotweb.android.webview.SpaccWebView;
|
import org.eu.spacc.spaccdotweb.android.webview.SpaccWebView;
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
public class SpaccWebViewActivity extends Activity {
|
public class SpaccWebViewActivity extends Activity {
|
||||||
protected SpaccWebView webView;
|
protected SpaccWebView webView;
|
||||||
|
@ -5,9 +5,10 @@ import android.content.res.XmlResourceParser;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.eu.spacc.spaccdotweb.android.Constants.*;
|
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
import org.eu.spacc.spaccdotweb.android.Constants.*;
|
||||||
|
|
||||||
public class ConfigReader {
|
public class ConfigReader {
|
||||||
private final Map<String, Object> configData = new HashMap<>();
|
private final Map<String, Object> configData = new HashMap<>();
|
||||||
|
|
||||||
|
@ -3,18 +3,17 @@ package org.eu.spacc.spaccdotweb.android.helpers;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.eu.spacc.spaccdotweb.android.Constants;
|
import org.eu.spacc.spaccdotweb.android.Constants;
|
||||||
import org.eu.spacc.spaccdotweb.android.utils.StorageUtils;
|
import org.eu.spacc.spaccdotweb.android.utils.StorageUtils;
|
||||||
import org.eu.spacc.spaccdotweb.android.utils.FileUtils;
|
import org.eu.spacc.spaccdotweb.android.utils.FileUtils;
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class DataMoveHelper {
|
public class DataMoveHelper {
|
||||||
|
|
||||||
public static void run(Context context, int labelExit, int dialogTitle, int dialogMessage) {
|
public static void run(Context context, int labelExit, int dialogTitle, int dialogMessage) {
|
||||||
Activity activity = (Activity)context;
|
|
||||||
SharedPrefHelper sharedPrefHelper = new SharedPrefHelper(context);
|
SharedPrefHelper sharedPrefHelper = new SharedPrefHelper(context);
|
||||||
Constants.DataLocation dataLocationReal = (StorageUtils.isInstalledOnExternalStorage(context) ? Constants.DataLocation.EXTERNAL : Constants.DataLocation.INTERNAL);
|
Constants.DataLocation dataLocationReal = (StorageUtils.isInstalledOnExternalStorage(context) ? Constants.DataLocation.EXTERNAL : Constants.DataLocation.INTERNAL);
|
||||||
Integer dataLocationSaved = sharedPrefHelper.getInt("data_location");
|
Integer dataLocationSaved = sharedPrefHelper.getInt("data_location");
|
||||||
@ -22,29 +21,21 @@ public class DataMoveHelper {
|
|||||||
sharedPrefHelper.setInt("data_location", dataLocationReal.ordinal());
|
sharedPrefHelper.setInt("data_location", dataLocationReal.ordinal());
|
||||||
} else if (!dataLocationSaved.equals(dataLocationReal.ordinal())) {
|
} else if (!dataLocationSaved.equals(dataLocationReal.ordinal())) {
|
||||||
new AlertDialog.Builder(context)
|
new AlertDialog.Builder(context)
|
||||||
.setTitle(dialogTitle)
|
.setTitle(dialogTitle)
|
||||||
.setMessage(dialogMessage)
|
.setMessage(dialogMessage)
|
||||||
.setCancelable(false)
|
.setCancelable(false)
|
||||||
.setNegativeButton(labelExit, new DialogInterface.OnClickListener() {
|
.setNegativeButton(labelExit, (dialogInterface, i) -> ((Activity)context).finish())
|
||||||
@Override
|
.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> {
|
||||||
public void onClick(DialogInterface dialogInterface, int i) {
|
// TODO: Check that the storage locations are all present to copy, and implement an error dialog
|
||||||
((Activity)context).finish();
|
try {
|
||||||
}
|
FileUtils.moveDirectory(StorageUtils.dataDirFromEnum(context, Constants.DataLocation.values()[dataLocationSaved]), StorageUtils.dataDirFromEnum(context, dataLocationReal), false);
|
||||||
})
|
} catch (IOException e) {
|
||||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
throw new RuntimeException(e);
|
||||||
@Override
|
}
|
||||||
public void onClick(DialogInterface dialogInterface, int i) {
|
sharedPrefHelper.setInt("data_location", dataLocationReal.ordinal());
|
||||||
// TODO: Check that the storage locations are all present to copy, and implement an error dialog
|
restartActivity(context);
|
||||||
try {
|
})
|
||||||
FileUtils.moveDirectory(StorageUtils.dataDirFromEnum(context, Constants.DataLocation.values()[dataLocationSaved]), StorageUtils.dataDirFromEnum(context, dataLocationReal), false);
|
.show();
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
sharedPrefHelper.setInt("data_location", dataLocationReal.ordinal());
|
|
||||||
restartActivity(context);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.show();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,9 @@ package org.eu.spacc.spaccdotweb.android.helpers;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class SharedPrefHelper {
|
public class SharedPrefHelper {
|
||||||
private final SharedPreferences sharedPref;
|
private final SharedPreferences sharedPref;
|
||||||
@ -11,8 +14,16 @@ public class SharedPrefHelper {
|
|||||||
this.sharedPref = context.getSharedPreferences("SpaccWebView", Context.MODE_PRIVATE);
|
this.sharedPref = context.getSharedPreferences("SpaccWebView", Context.MODE_PRIVATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SharedPrefHelper(Context context, String name) {
|
||||||
|
this.sharedPref = context.getSharedPreferences(name, Context.MODE_PRIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
public Integer getInt(String name) {
|
public Integer getInt(String name) {
|
||||||
Integer value = (Integer)sharedPref.getInt(name, -1);
|
return getInt(name, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getInt(String name, int fallback) {
|
||||||
|
Integer value = (Integer)sharedPref.getInt(name, fallback);
|
||||||
return (value != -1 ? value : null);
|
return (value != -1 ? value : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,4 +35,30 @@ public class SharedPrefHelper {
|
|||||||
editor.commit();
|
editor.commit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ArrayList<String> getStringList(String name) {
|
||||||
|
try {
|
||||||
|
String json = sharedPref.getString(name, null);
|
||||||
|
if (json != null) {
|
||||||
|
JSONArray parsed = new JSONArray(json);
|
||||||
|
ArrayList<String> restored = new ArrayList<>(parsed.length());
|
||||||
|
for (int i = 0; i < parsed.length(); i++) {
|
||||||
|
restored.add(parsed.getString(i));
|
||||||
|
}
|
||||||
|
return restored;
|
||||||
|
}
|
||||||
|
} catch (JSONException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStringList(String name, ArrayList<String> list) {
|
||||||
|
SharedPreferences.Editor editor = sharedPref.edit().putString(name, new JSONArray(list).toString());
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
|
||||||
|
editor.apply();
|
||||||
|
} else {
|
||||||
|
editor.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,14 +14,19 @@ public class ApiUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Boolean isInternalUrl(Uri url) {
|
||||||
|
return url.toString().startsWith("file:///android_asset/");
|
||||||
|
}
|
||||||
|
|
||||||
public static void openOrShareUrl(Context context, Uri url) {
|
public static void openOrShareUrl(Context context, Uri url) {
|
||||||
try {
|
if (!isInternalUrl(url)) {
|
||||||
// Open the URL externally
|
try { // Open the URL externally
|
||||||
context.startActivity(new Intent(Intent.ACTION_VIEW, url));
|
context.startActivity(new Intent(Intent.ACTION_VIEW, url));
|
||||||
} catch (ActivityNotFoundException ignored) {
|
return;
|
||||||
// No app can handle it, so share it instead
|
} catch (ActivityNotFoundException ignored) {}
|
||||||
context.startActivity(new Intent(Intent.ACTION_SEND).setType("text/plain").putExtra(Intent.EXTRA_TEXT, url.toString()));
|
|
||||||
}
|
}
|
||||||
|
// No app can handle it, so share it instead
|
||||||
|
context.startActivity(new Intent(Intent.ACTION_SEND).setType("text/plain").putExtra(Intent.EXTRA_TEXT, url.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void writeToClipboard(Context context, String text) {
|
public static void writeToClipboard(Context context, String text) {
|
||||||
|
@ -6,6 +6,7 @@ import android.content.Context;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.webkit.CookieManager;
|
import android.webkit.CookieManager;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
@ -67,6 +68,24 @@ public class FileUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* https://gist.github.com/defHLT/3ac50c765f3cf289da03 */
|
||||||
|
public static String inputStreamToString(InputStream inputStream) {
|
||||||
|
String res = null;
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
byte[] b = new byte[1];
|
||||||
|
try {
|
||||||
|
while (inputStream.read(b) != -1) {
|
||||||
|
outputStream.write(b);
|
||||||
|
}
|
||||||
|
res = outputStream.toString();
|
||||||
|
inputStream.close();
|
||||||
|
outputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Handle downloads internally on old Android versions
|
// TODO: Handle downloads internally on old Android versions
|
||||||
public static void startFileDownload(Context context, Uri downloadUrl, String userAgent, String contentDisposition, String mimeType) {
|
public static void startFileDownload(Context context, Uri downloadUrl, String userAgent, String contentDisposition, String mimeType) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD && !downloadUrl.toString().toLowerCase().startsWith("data:")) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD && !downloadUrl.toString().toLowerCase().startsWith("data:")) {
|
||||||
|
@ -6,6 +6,7 @@ import android.content.pm.PackageManager;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import org.eu.spacc.spaccdotweb.android.Constants.*;
|
import org.eu.spacc.spaccdotweb.android.Constants.*;
|
||||||
|
|
||||||
public class StorageUtils {
|
public class StorageUtils {
|
||||||
|
@ -2,6 +2,7 @@ package org.eu.spacc.spaccdotweb.android.webview;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
import org.eu.spacc.spaccdotweb.android.utils.FileUtils;
|
import org.eu.spacc.spaccdotweb.android.utils.FileUtils;
|
||||||
|
|
||||||
public class DownloadListener implements android.webkit.DownloadListener {
|
public class DownloadListener implements android.webkit.DownloadListener {
|
||||||
|
@ -1,24 +1,37 @@
|
|||||||
package org.eu.spacc.spaccdotweb.android.webview;
|
package org.eu.spacc.spaccdotweb.android.webview;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.webkit.PermissionRequest;
|
||||||
import android.webkit.ValueCallback;
|
import android.webkit.ValueCallback;
|
||||||
import android.webkit.WebChromeClient;
|
import android.webkit.WebChromeClient;
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
import org.eu.spacc.spaccdotweb.android.Config;
|
||||||
import org.eu.spacc.spaccdotweb.android.Constants;
|
import org.eu.spacc.spaccdotweb.android.Constants;
|
||||||
import org.eu.spacc.spaccdotweb.android.SpaccWebViewActivity;
|
import org.eu.spacc.spaccdotweb.android.SpaccWebViewActivity;
|
||||||
|
import org.eu.spacc.spaccdotweb.android.utils.ApiUtils;
|
||||||
|
|
||||||
public class SpaccWebChromeClient extends WebChromeClient {
|
public class SpaccWebChromeClient extends WebChromeClient {
|
||||||
private final SpaccWebViewActivity activity;
|
private final SpaccWebViewActivity activity;
|
||||||
|
private Config config;
|
||||||
|
|
||||||
public SpaccWebChromeClient(SpaccWebViewActivity activity) {
|
public SpaccWebChromeClient(SpaccWebViewActivity activity) {
|
||||||
super();
|
super();
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void applyConfig(Config config) {
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Android < 4 support
|
// TODO: Android < 4 support
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
@ -37,4 +50,27 @@ public class SpaccWebChromeClient extends WebChromeClient {
|
|||||||
activity.fileUploadCallback = valueCallback;
|
activity.fileUploadCallback = valueCallback;
|
||||||
activity.startActivityForResult(Intent.createChooser(intent, null), Constants.ActivityCodes.UPLOAD_FILE.ordinal());
|
activity.startActivityForResult(Intent.createChooser(intent, null), Constants.ActivityCodes.UPLOAD_FILE.ordinal());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPermissionRequest(PermissionRequest request) {
|
||||||
|
AtomicBoolean handled = new AtomicBoolean(false);
|
||||||
|
ApiUtils.apiRun(21, () -> {
|
||||||
|
ArrayList<String> granted = new ArrayList<>();
|
||||||
|
for (String resource: request.getResources()) {
|
||||||
|
if ((resource.equals(PermissionRequest.RESOURCE_PROTECTED_MEDIA_ID) && config.getAllowDrmMedia()) ||
|
||||||
|
(resource.equals(PermissionRequest.RESOURCE_AUDIO_CAPTURE) && config.getAllowAudioCapture()) ||
|
||||||
|
(resource.equals(PermissionRequest.RESOURCE_VIDEO_CAPTURE) && config.getAllowVideoCapture())
|
||||||
|
) {
|
||||||
|
granted.add(resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!granted.isEmpty()) {
|
||||||
|
request.grant(granted.toArray(new String[0]));
|
||||||
|
handled.set(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!handled.get()) {
|
||||||
|
super.onPermissionRequest(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,15 @@ package org.eu.spacc.spaccdotweb.android.webview;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.ContextMenu;
|
import android.view.ContextMenu;
|
||||||
|
import android.webkit.WebChromeClient;
|
||||||
import android.webkit.WebSettings;
|
import android.webkit.WebSettings;
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
|
import android.webkit.WebViewClient;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import org.eu.spacc.spaccdotweb.android.Config;
|
import org.eu.spacc.spaccdotweb.android.Config;
|
||||||
import org.eu.spacc.spaccdotweb.android.SpaccWebViewActivity;
|
import org.eu.spacc.spaccdotweb.android.SpaccWebViewActivity;
|
||||||
import org.eu.spacc.spaccdotweb.android.helpers.ConfigReader;
|
import org.eu.spacc.spaccdotweb.android.helpers.ConfigReader;
|
||||||
@ -14,36 +19,111 @@ import org.eu.spacc.spaccdotweb.android.utils.ApiUtils;
|
|||||||
public class SpaccWebView extends WebView {
|
public class SpaccWebView extends WebView {
|
||||||
private Config config;
|
private Config config;
|
||||||
private Context context;
|
private Context context;
|
||||||
|
private SpaccWebViewClient webViewClient;
|
||||||
|
private SpaccWebChromeClient webChromeClient;
|
||||||
|
|
||||||
|
private int openString;
|
||||||
|
private int openExternallyString;
|
||||||
|
private int copyUrlString;
|
||||||
|
|
||||||
|
private Boolean isLoaded = false;
|
||||||
|
protected ArrayList<String> scriptQueue = new ArrayList<>();
|
||||||
|
|
||||||
|
public SpaccWebView(Context context) {
|
||||||
|
super(context);
|
||||||
|
setup(context);
|
||||||
|
}
|
||||||
|
|
||||||
public SpaccWebView(Context context, AttributeSet attrs) {
|
public SpaccWebView(Context context, AttributeSet attrs) {
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
|
setup(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setup(Context context) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.setWebViewClient(new SpaccWebViewClient(context));
|
this.setWebViewClient(this.webViewClient = new SpaccWebViewClient(context));
|
||||||
this.setWebChromeClient(new SpaccWebChromeClient((SpaccWebViewActivity)context));
|
this.setWebChromeClient(this.webChromeClient = new SpaccWebChromeClient((SpaccWebViewActivity)context));
|
||||||
this.setDownloadListener(new DownloadListener(context));
|
this.setDownloadListener(new DownloadListener(context));
|
||||||
this.config = new Config();
|
this.config = new Config();
|
||||||
this.applyConfig(context);
|
this.applyConfig(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setStrings(int open, int openExternally, int copyUrl) {
|
||||||
|
openString = open;
|
||||||
|
openExternallyString = openExternally;
|
||||||
|
copyUrlString = copyUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setWebViewClient(WebViewClient client) {
|
||||||
|
super.setWebViewClient(client);
|
||||||
|
webViewClient = (SpaccWebViewClient)client;
|
||||||
|
webViewClient.applyConfig(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setWebChromeClient(WebChromeClient client) {
|
||||||
|
super.setWebChromeClient(client);
|
||||||
|
webChromeClient = (SpaccWebChromeClient)client;
|
||||||
|
webChromeClient.applyConfig(config);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Implement context menu (long-press on links, images, etc...)
|
// TODO: Implement context menu (long-press on links, images, etc...)
|
||||||
@Override
|
@Override
|
||||||
protected void onCreateContextMenu(ContextMenu menu) {
|
protected void onCreateContextMenu(ContextMenu menu) {
|
||||||
super.onCreateContextMenu(menu);
|
super.onCreateContextMenu(menu);
|
||||||
HitTestResult result = getHitTestResult();
|
HitTestResult result = getHitTestResult();
|
||||||
switch (result.getType()) {
|
/*switch (result.getType()) {
|
||||||
|
case HitTestResult.UNKNOWN_TYPE:
|
||||||
|
case HitTestResult.IMAGE_TYPE:
|
||||||
case HitTestResult.SRC_ANCHOR_TYPE:
|
case HitTestResult.SRC_ANCHOR_TYPE:
|
||||||
case HitTestResult.SRC_IMAGE_ANCHOR_TYPE:
|
case HitTestResult.SRC_IMAGE_ANCHOR_TYPE:*/
|
||||||
String href = result.getExtra();
|
String href = result.getExtra();
|
||||||
menu.setHeaderTitle(href);
|
if (href != null) {
|
||||||
menu.add("Copy URL").setOnMenuItemClickListener(menuItem -> {
|
menu.setHeaderTitle(href);
|
||||||
ApiUtils.writeToClipboard(context, href);
|
menu.add(openString).setOnMenuItemClickListener(menuItem -> {
|
||||||
return false;
|
if (!webViewClient.shouldOverrideUrlLoading(this, href)) {
|
||||||
});
|
this.loadUrl(href);
|
||||||
menu.add("Open or Share Externally").setOnMenuItemClickListener(menuItem -> {
|
}
|
||||||
ApiUtils.openOrShareUrl(context, Uri.parse(href));
|
return false;
|
||||||
return false;
|
});
|
||||||
});
|
if (!ApiUtils.isInternalUrl(Uri.parse(href))) {
|
||||||
break;
|
menu.add(openExternallyString).setOnMenuItemClickListener(menuItem -> {
|
||||||
|
ApiUtils.openOrShareUrl(context, Uri.parse(href));
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
menu.add(copyUrlString).setOnMenuItemClickListener(menuItem -> {
|
||||||
|
ApiUtils.writeToClipboard(context, href);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/*break;
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
public void injectScript(String script) {
|
||||||
|
if (isLoaded) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
|
this.evaluateJavascript(script, null);
|
||||||
|
} else {
|
||||||
|
this.loadUrl("javascript:(function(){" + script + "})();");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
scriptQueue.add(script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void injectStyle(String style) {
|
||||||
|
injectScript("document.head.appendChild(Object.assign(document.createElement('style'),{innerHTML:\"" + style + "\"}))");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setLoaded(Boolean loaded) {
|
||||||
|
if (isLoaded = loaded) {
|
||||||
|
for (String script : scriptQueue) {
|
||||||
|
injectScript(script);
|
||||||
|
}
|
||||||
|
scriptQueue.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,11 +141,16 @@ public class SpaccWebView extends WebView {
|
|||||||
|
|
||||||
ApiUtils.apiRun(3, () -> webSettings.setAllowFileAccess(false));
|
ApiUtils.apiRun(3, () -> webSettings.setAllowFileAccess(false));
|
||||||
|
|
||||||
|
ApiUtils.apiRun(17, () -> webSettings.setMediaPlaybackRequiresUserGesture(!config.getAllowAutoplay()));
|
||||||
|
|
||||||
webSettings.setStandardFontFamily(config.getStandardFontFamily());
|
webSettings.setStandardFontFamily(config.getStandardFontFamily());
|
||||||
ApiUtils.apiRun(3, () -> webSettings.setUserAgentString(config.getUserAgent()));
|
ApiUtils.apiRun(3, () -> webSettings.setUserAgentString(config.getUserAgent()));
|
||||||
|
|
||||||
ApiUtils.apiRun(3, () -> webSettings.setBuiltInZoomControls(config.getAllowZoomControls()));
|
ApiUtils.apiRun(3, () -> webSettings.setBuiltInZoomControls(config.getAllowZoomControls()));
|
||||||
ApiUtils.apiRun(11, () -> webSettings.setDisplayZoomControls(config.getDisplayZoomControls()));
|
ApiUtils.apiRun(11, () -> webSettings.setDisplayZoomControls(config.getDisplayZoomControls()));
|
||||||
|
|
||||||
|
webViewClient.applyConfig(config);
|
||||||
|
webChromeClient.applyConfig(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadConfig(Context context, int configResource) {
|
public void loadConfig(Context context, int configResource) {
|
||||||
@ -73,6 +158,10 @@ public class SpaccWebView extends WebView {
|
|||||||
this.applyConfig(context);
|
this.applyConfig(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Config getConfig() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
public void loadAppIndex() {
|
public void loadAppIndex() {
|
||||||
String url = null;
|
String url = null;
|
||||||
switch (config.getAppIndex()) {
|
switch (config.getAppIndex()) {
|
||||||
|
@ -2,31 +2,49 @@ package org.eu.spacc.spaccdotweb.android.webview;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
import android.webkit.WebViewClient;
|
import android.webkit.WebViewClient;
|
||||||
|
|
||||||
import org.eu.spacc.spaccdotweb.android.utils.ApiUtils;
|
|
||||||
|
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
|
||||||
|
import org.eu.spacc.spaccdotweb.android.Config;
|
||||||
|
import org.eu.spacc.spaccdotweb.android.Constants;
|
||||||
|
import org.eu.spacc.spaccdotweb.android.utils.ApiUtils;
|
||||||
|
|
||||||
public class SpaccWebViewClient extends WebViewClient {
|
public class SpaccWebViewClient extends WebViewClient {
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
private Config config;
|
||||||
|
|
||||||
public SpaccWebViewClient(Context context) {
|
public SpaccWebViewClient(Context context) {
|
||||||
super();
|
super();
|
||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void applyConfig(Config config) {
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||||
|
super.onPageStarted(view, url, favicon);
|
||||||
|
((SpaccWebView)view).setLoaded(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageFinished(WebView view, String url) {
|
||||||
|
super.onPageFinished(view, url);
|
||||||
|
((SpaccWebView)view).setLoaded(true);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||||
// TODO: This should not override all HTTP links if the app loads from remote (which will allow proper internal navigation and file downloads)
|
// TODO: This should not override all HTTP links if the app loads from remote (which will allow proper internal navigation and file downloads)
|
||||||
// NOTE: It seems like the WebView overrides loading of data: URIs before we can get it here...
|
// NOTE: It seems like the WebView overrides loading of data: URIs before we can get it here...
|
||||||
List<String> externalProtocols = Arrays.asList("data", "http", "https", "mailto", "ftp");
|
// List<String> externalProtocols = Arrays.asList("data", "http", "https", "mailto", "ftp");
|
||||||
String protocol = url.toLowerCase().split(":")[0];
|
String protocol = url.toLowerCase().split(":")[0];
|
||||||
if (protocol.equals("file")) {
|
if (protocol.equals("file") || (config.getAppIndex() == Constants.AppIndex.REMOTE && Arrays.asList("http", "https").contains(protocol))) {
|
||||||
return super.shouldOverrideUrlLoading(view, url);
|
return super.shouldOverrideUrlLoading(view, url);
|
||||||
} else if (protocol.equals("intent")) {
|
} else if (protocol.equals("intent")) {
|
||||||
ApiUtils.apiRun(4, () -> {
|
ApiUtils.apiRun(4, () -> {
|
||||||
@ -42,6 +60,5 @@ public class SpaccWebViewClient extends WebViewClient {
|
|||||||
ApiUtils.openOrShareUrl(context, Uri.parse(url));
|
ApiUtils.openOrShareUrl(context, Uri.parse(url));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<android.widget.LinearLayout
|
<android.widget.LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/root"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".MainActivity">
|
tools:context=".MainActivity">
|
||||||
|
13
SpaccDotWeb.Android/app/src/main/res/menu/main.xml
Normal file
13
SpaccDotWeb.Android/app/src/main/res/menu/main.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:id="@+id/reload" android:title="@string/reload_menu" />
|
||||||
|
<item android:id="@+id/stop" android:title="@string/stop_loading_menu" />
|
||||||
|
<item android:id="@+id/backwards" android:title="@string/backwards_menu" />
|
||||||
|
<item android:id="@+id/forward" android:title="@string/forward_menu" />
|
||||||
|
<item android:id="@+id/open_externally" android:title="@string/open_externally_menu" />
|
||||||
|
<item android:id="@+id/copy_url" android:title="@string/copy_url_menu" />
|
||||||
|
<item android:id="@+id/exec_script" android:title="@string/execute_javascript_menu" />
|
||||||
|
<item android:id="@+id/hide" android:title="@string/hide_menu" />
|
||||||
|
<item android:id="@+id/exit" android:title="@string/exit_menu" />
|
||||||
|
<item android:id="@+id/about_app" android:title="@string/about_app_menu" />
|
||||||
|
</menu>
|
@ -1,6 +1,44 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE resources [
|
||||||
|
<!ENTITY about_app "Informazioni App">
|
||||||
|
<!ENTITY exit "Esci">
|
||||||
|
<!ENTITY hide "Nascondi">
|
||||||
|
<!ENTITY reload "Ricarica">
|
||||||
|
<!ENTITY stop_loading "Ferma Caricamento">
|
||||||
|
<!ENTITY backwards "Indietro">
|
||||||
|
<!ENTITY forward "Avanti">
|
||||||
|
<!ENTITY open "Apri">
|
||||||
|
<!ENTITY open_externally "&open; Esternamente">
|
||||||
|
<!ENTITY copy_url "Copia URL">
|
||||||
|
<!ENTITY execute_javascript "Esegui JavaScript">
|
||||||
|
]>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="exit">Esci</string>
|
<string name="about_app">&about_app;</string>
|
||||||
|
<string name="about_app_menu">ℹ️ &about_app;</string>
|
||||||
|
<string name="yes">Yes</string>
|
||||||
|
<string name="no">No</string>
|
||||||
|
<string name="cancel">Cancel</string>
|
||||||
|
<string name="exit">&exit;</string>
|
||||||
|
<string name="exit_menu">🚪 &exit;</string>
|
||||||
|
<string name="hide">&hide;</string>
|
||||||
|
<string name="hide_menu">🕳️ &hide;</string>
|
||||||
<string name="move_app_data">Sposta Dati App</string>
|
<string name="move_app_data">Sposta Dati App</string>
|
||||||
<string name="move_app_data_info">La app è stata trasferita su una diversa locazione di archiviazione. I dati saranno spostati ora.</string>
|
<string name="move_app_data_info">La app è stata trasferita su una diversa locazione di archiviazione. I dati saranno spostati ora.</string>
|
||||||
|
<string name="loading">Caricamento</string>
|
||||||
|
<string name="reload">&reload;</string>
|
||||||
|
<string name="reload_menu">🔄 &reload;</string>
|
||||||
|
<string name="stop_loading">&stop_loading;</string>
|
||||||
|
<string name="stop_loading_menu">🛑 &stop_loading;</string>
|
||||||
|
<string name="backwards">&backwards;</string>
|
||||||
|
<string name="backwards_menu">⏮️ &backwards;</string>
|
||||||
|
<string name="forward">&forward;</string>
|
||||||
|
<string name="forward_menu">⏭️ &forward;</string>
|
||||||
|
<string name="open">&open;</string>
|
||||||
|
<string name="open_menu">👌 &open;</string>
|
||||||
|
<string name="open_externally">&open_externally;</string>
|
||||||
|
<string name="open_externally_menu">☝️ &open_externally;</string>
|
||||||
|
<string name="copy_url">©_url;</string>
|
||||||
|
<string name="copy_url_menu">🔗 ©_url;</string>
|
||||||
|
<string name="execute_javascript">&execute_javascript;</string>
|
||||||
|
<string name="execute_javascript_menu">🔣 &execute_javascript;</string>
|
||||||
</resources>
|
</resources>
|
@ -1,7 +1,46 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE resources [
|
||||||
|
<!ENTITY app_name "SpaccWebView Application">
|
||||||
|
<!ENTITY about_app "About App">
|
||||||
|
<!ENTITY exit "Exit">
|
||||||
|
<!ENTITY hide "Hide">
|
||||||
|
<!ENTITY reload "Reload">
|
||||||
|
<!ENTITY stop_loading "Stop Loading">
|
||||||
|
<!ENTITY backwards "Backwards">
|
||||||
|
<!ENTITY forward "Forward">
|
||||||
|
<!ENTITY open "Open">
|
||||||
|
<!ENTITY open_externally "&open; Externally">
|
||||||
|
<!ENTITY copy_url "Copy URL">
|
||||||
|
<!ENTITY execute_javascript "Execute JavaScript">
|
||||||
|
]>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name" translatable="false">SpaccWebView Application</string>
|
<string name="app_name" translatable="false">&app_name;</string>
|
||||||
<string name="exit">Exit</string>
|
<string name="about_app">&about_app;</string>
|
||||||
|
<string name="about_app_menu">ℹ️ &about_app;</string>
|
||||||
|
<string name="yes">Yes</string>
|
||||||
|
<string name="no">No</string>
|
||||||
|
<string name="cancel">Cancel</string>
|
||||||
|
<string name="exit">&exit;</string>
|
||||||
|
<string name="exit_menu">🚪 &exit;</string>
|
||||||
|
<string name="hide">&hide;</string>
|
||||||
|
<string name="hide_menu">🕳️ &hide;</string>
|
||||||
<string name="move_app_data">Move App Data</string>
|
<string name="move_app_data">Move App Data</string>
|
||||||
<string name="move_app_data_info">The app has been transferred to a different storage location. The data will be moved now.</string>
|
<string name="move_app_data_info">The app has been transferred to a different storage location. The data will be moved now.</string>
|
||||||
|
<string name="loading">Loading</string>
|
||||||
|
<string name="reload">&reload;</string>
|
||||||
|
<string name="reload_menu">🔄 &reload;</string>
|
||||||
|
<string name="stop_loading">&stop_loading;</string>
|
||||||
|
<string name="stop_loading_menu">🛑 &stop_loading;</string>
|
||||||
|
<string name="backwards">&backwards;</string>
|
||||||
|
<string name="backwards_menu">⏮️ &backwards;</string>
|
||||||
|
<string name="forward">&forward;</string>
|
||||||
|
<string name="forward_menu">⏭️ &forward;</string>
|
||||||
|
<string name="open">&open;</string>
|
||||||
|
<string name="open_menu">👌 &open;</string>
|
||||||
|
<string name="open_externally">&open_externally;</string>
|
||||||
|
<string name="open_externally_menu">☝️ &open_externally;</string>
|
||||||
|
<string name="copy_url">©_url;</string>
|
||||||
|
<string name="copy_url_menu">🔗 ©_url;</string>
|
||||||
|
<string name="execute_javascript">&execute_javascript;</string>
|
||||||
|
<string name="execute_javascript_menu">🔣 &execute_javascript;</string>
|
||||||
</resources>
|
</resources>
|
@ -1,8 +1,21 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<config>
|
<config>
|
||||||
|
<!-- <string name="about_page">https://example.com/apps/TheApp</string> --> <!-- replace or leave empty -->
|
||||||
|
|
||||||
<AppIndex name="app_index">local</AppIndex> <!-- local or remote -->
|
<AppIndex name="app_index">local</AppIndex> <!-- local or remote -->
|
||||||
<string name="local_index">index.html</string>
|
<string name="local_index">index.html</string>
|
||||||
<string name="remote_index">https://example.com</string>
|
<string name="remote_index">https://example.com/</string>
|
||||||
|
|
||||||
|
<boolean name="allow_javascript">true</boolean>
|
||||||
|
<boolean name="allow_storage">true</boolean>
|
||||||
|
<boolean name="allow_autoplay">true</boolean>
|
||||||
|
<boolean name="allow_drm_media">true</boolean>
|
||||||
|
<boolean name="allow_audio_capture">false</boolean>
|
||||||
|
<boolean name="allow_video_capture">false</boolean>
|
||||||
|
|
||||||
|
<boolean name="allow_zoom_controls">false</boolean>
|
||||||
|
<boolean name="display_zoom_controls">false</boolean>
|
||||||
|
|
||||||
<!-- <string name="standard_font_family"></string> -->
|
<!-- <string name="standard_font_family"></string> -->
|
||||||
<!-- <string name="user_agent"></string> -->
|
<!-- <string name="user_agent"></string> -->
|
||||||
</config>
|
</config>
|
@ -9,6 +9,7 @@ pluginManagement {
|
|||||||
plugins {
|
plugins {
|
||||||
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0'
|
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencyResolutionManagement {
|
dependencyResolutionManagement {
|
||||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
repositories {
|
repositories {
|
||||||
@ -16,5 +17,6 @@ dependencyResolutionManagement {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rootProject.name = 'SpaccWebView Application'
|
rootProject.name = 'SpaccWebView Application'
|
||||||
include ':app'
|
include ':app'
|
||||||
|
Reference in New Issue
Block a user