mirror of
https://gitlab.com/SpaccInc/SpaccDotWeb.git
synced 2025-06-05 21:29:12 +02:00
[Android] Initial upload support, Context menu for links, More config options
This commit is contained in:
@ -27,3 +27,9 @@ android {
|
||||
|
||||
compileSdk 34
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(17)
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,8 @@
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:installLocation="auto">
|
||||
android:installLocation="auto"
|
||||
tools:ignore="UnusedAttribute">
|
||||
|
||||
<!-- Lets the app access the Internet — not needed for fully offline apps -->
|
||||
<!-- <uses-permission android:name="android.permission.INTERNET" /> -->
|
||||
|
@ -27,7 +27,7 @@
|
||||
<li><a href="intent://#Intent;action=android.intent.action.VIEW;scheme=http;type=video/mp4;end">intent://</a></li>
|
||||
</ul>
|
||||
<h3>Files</h3>
|
||||
<p>Upload: <label><input type="file"/></label></p>
|
||||
<p>Upload: <label><input type="file" onchange="(function(f){ alert(f.name + ' ' + f.size + ' ' + f.type); })(this.files[0]);" /></label></p>
|
||||
<p>Download:</p>
|
||||
<ul>
|
||||
<li><a download="Hello World.txt" href="data:text/plain;utf8,Hello World!">data:, .txt</a></li>
|
||||
|
@ -1,5 +1,7 @@
|
||||
package org.eu.spacc.spaccdotweb.android;
|
||||
|
||||
import android.webkit.WebSettings;
|
||||
|
||||
import org.eu.spacc.spaccdotweb.android.Constants.*;
|
||||
import org.eu.spacc.spaccdotweb.android.helpers.ConfigReader;
|
||||
|
||||
@ -22,6 +24,16 @@ public class Config extends Defaults {
|
||||
return (value != null ? value : Defaults.ALLOW_STORAGE);
|
||||
}
|
||||
|
||||
public Boolean getAllowZoomControls() {
|
||||
Boolean value = getBoolean("allow_zoom_controls");
|
||||
return (value != null ? value : Defaults.ALLOW_ZOOM_CONTROLS);
|
||||
}
|
||||
|
||||
public Boolean getDisplayZoomControls() {
|
||||
Boolean value = getBoolean("display_zoom_controls");
|
||||
return (value != null ? value : Defaults.DISPLAY_ZOOM_CONTROLS);
|
||||
}
|
||||
|
||||
public AppIndex getAppIndex() {
|
||||
AppIndex value = (AppIndex)get("app_index");
|
||||
return (value != null ? value : Defaults.APP_INDEX);
|
||||
@ -36,12 +48,18 @@ public class Config extends Defaults {
|
||||
return getString("remote_index");
|
||||
}
|
||||
|
||||
private Object get(String key) {
|
||||
if (configReader != null) {
|
||||
return configReader.get(key);
|
||||
} else {
|
||||
return null;
|
||||
public String getStandardFontFamily() {
|
||||
return getString("standard_font_family");
|
||||
}
|
||||
|
||||
public String getUserAgent() {
|
||||
return getString("user_agent");
|
||||
}
|
||||
|
||||
private Object get(String key) {
|
||||
return (configReader != null
|
||||
? configReader.get(key)
|
||||
: null);
|
||||
}
|
||||
|
||||
private Boolean getBoolean(String key) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
package org.eu.spacc.spaccdotweb.android;
|
||||
|
||||
public class Constants {
|
||||
//public enum ActivityCodes { DOWNLOAD_FILE, UPLOAD_FILE }
|
||||
public enum ActivityCodes { UPLOAD_FILE /* , DOWNLOAD_FILE */ }
|
||||
public enum AppIndex { LOCAL, REMOTE }
|
||||
public enum DataLocation { INTERNAL, EXTERNAL }
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import org.eu.spacc.spaccdotweb.android.Constants.*;
|
||||
public class Defaults {
|
||||
public static final Boolean ALLOW_JAVASCRIPT = true;
|
||||
public static final Boolean ALLOW_STORAGE = true;
|
||||
public static final Boolean ALLOW_ZOOM_CONTROLS = false;
|
||||
public static final Boolean DISPLAY_ZOOM_CONTROLS = false;
|
||||
public static final AppIndex APP_INDEX = AppIndex.LOCAL;
|
||||
public static final String LOCAL_INDEX = "index.html";
|
||||
}
|
||||
|
@ -1,23 +1,49 @@
|
||||
package org.eu.spacc.spaccdotweb.android;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.webkit.ValueCallback;
|
||||
import org.eu.spacc.spaccdotweb.android.webview.SpaccWebChromeClient;
|
||||
import org.eu.spacc.spaccdotweb.android.webview.SpaccWebView;
|
||||
import java.io.File;
|
||||
|
||||
public class SpaccWebViewActivity extends Activity {
|
||||
protected SpaccWebView webView;
|
||||
public ValueCallback<Uri> fileUploadCallback;
|
||||
public ValueCallback<Uri[]> filesUploadCallback;
|
||||
|
||||
// @Override
|
||||
// protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
// super.onActivityResult(requestCode, resultCode, data);
|
||||
@TargetApi(Build.VERSION_CODES.ECLAIR_MR1)
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
// if (requestCode == Constants.CREATE_FILE_REQUEST_CODE && resultCode == RESULT_OK && data != null) {
|
||||
// Uri fileUri = data.getData();
|
||||
// if (fileUri != null) {
|
||||
// enqueueDownload(Uri.parse(fileUri.toString()));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
if (requestCode == Constants.ActivityCodes.UPLOAD_FILE.ordinal()) {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && filesUploadCallback != null) {
|
||||
filesUploadCallback.onReceiveValue(SpaccWebChromeClient.FileChooserParams.parseResult(resultCode, data));
|
||||
} else if (fileUploadCallback != null) {
|
||||
fileUploadCallback.onReceiveValue(data.getData());
|
||||
}
|
||||
} else {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && filesUploadCallback != null) {
|
||||
filesUploadCallback.onReceiveValue(null);
|
||||
} else if (fileUploadCallback != null) {
|
||||
fileUploadCallback.onReceiveValue(null);
|
||||
}
|
||||
}
|
||||
fileUploadCallback = null;
|
||||
filesUploadCallback = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
|
@ -23,4 +23,15 @@ public class ApiUtils {
|
||||
context.startActivity(new Intent(Intent.ACTION_SEND).setType("text/plain").putExtra(Intent.EXTRA_TEXT, url.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeToClipboard(Context context, String text) {
|
||||
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB) {
|
||||
android.text.ClipboardManager clipboard = (android.text.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
clipboard.setText(text);
|
||||
} else {
|
||||
android.content.ClipboardManager clipboard = (android.content.ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
android.content.ClipData clip = android.content.ClipData.newPlainText(null, text);
|
||||
clipboard.setPrimaryClip(clip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,40 @@
|
||||
package org.eu.spacc.spaccdotweb.android.webview;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.webkit.ValueCallback;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import org.eu.spacc.spaccdotweb.android.Constants;
|
||||
import org.eu.spacc.spaccdotweb.android.SpaccWebViewActivity;
|
||||
|
||||
public class SpaccWebChromeClient extends WebChromeClient {
|
||||
private final SpaccWebViewActivity activity;
|
||||
|
||||
public SpaccWebChromeClient(SpaccWebViewActivity activity) {
|
||||
super();
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
// TODO: Android < 4 support
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
@Override
|
||||
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> valueCallback, FileChooserParams fileChooserParams) {
|
||||
activity.filesUploadCallback = valueCallback;
|
||||
activity.startActivityForResult(fileChooserParams.createIntent(), Constants.ActivityCodes.UPLOAD_FILE.ordinal());
|
||||
return true;
|
||||
}
|
||||
|
||||
//@Override // Android 4.1+
|
||||
protected void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType, String capture) {
|
||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intent.setType("*/*");
|
||||
activity.fileUploadCallback = valueCallback;
|
||||
activity.startActivityForResult(Intent.createChooser(intent, null), Constants.ActivityCodes.UPLOAD_FILE.ordinal());
|
||||
}
|
||||
}
|
@ -1,32 +1,51 @@
|
||||
package org.eu.spacc.spaccdotweb.android.webview;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ContextMenu;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
import org.eu.spacc.spaccdotweb.android.Config;
|
||||
import org.eu.spacc.spaccdotweb.android.SpaccWebViewActivity;
|
||||
import org.eu.spacc.spaccdotweb.android.helpers.ConfigReader;
|
||||
import org.eu.spacc.spaccdotweb.android.utils.ApiUtils;
|
||||
|
||||
public class SpaccWebView extends WebView {
|
||||
private Config config;
|
||||
private Context context;
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
public SpaccWebView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
this.setWebViewClient(new WebViewClient(context));
|
||||
this.setWebChromeClient(new WebChromeClient(context));
|
||||
this.context = context;
|
||||
this.setWebViewClient(new SpaccWebViewClient(context));
|
||||
this.setWebChromeClient(new SpaccWebChromeClient((SpaccWebViewActivity)context));
|
||||
this.setDownloadListener(new DownloadListener(context));
|
||||
this.config = new Config();
|
||||
this.applyConfig(context);
|
||||
}
|
||||
|
||||
// TODO: Implement context menu (long-press on links, images, etc...)
|
||||
// @Override
|
||||
// protected void onCreateContextMenu(ContextMenu menu) {
|
||||
// super.onCreateContextMenu(menu);
|
||||
// }
|
||||
@Override
|
||||
protected void onCreateContextMenu(ContextMenu menu) {
|
||||
super.onCreateContextMenu(menu);
|
||||
HitTestResult result = getHitTestResult();
|
||||
switch (result.getType()) {
|
||||
case HitTestResult.SRC_ANCHOR_TYPE:
|
||||
case HitTestResult.SRC_IMAGE_ANCHOR_TYPE:
|
||||
String href = result.getExtra();
|
||||
menu.setHeaderTitle(href);
|
||||
menu.add("Copy URL").setOnMenuItemClickListener(menuItem -> {
|
||||
ApiUtils.writeToClipboard(context, href);
|
||||
return false;
|
||||
});
|
||||
menu.add("Open or Share Externally").setOnMenuItemClickListener(menuItem -> {
|
||||
ApiUtils.openOrShareUrl(context, Uri.parse(href));
|
||||
return false;
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void applyConfig(Context context) {
|
||||
WebSettings webSettings = this.getSettings();
|
||||
@ -41,6 +60,12 @@ public class SpaccWebView extends WebView {
|
||||
}
|
||||
|
||||
ApiUtils.apiRun(3, () -> webSettings.setAllowFileAccess(false));
|
||||
|
||||
webSettings.setStandardFontFamily(config.getStandardFontFamily());
|
||||
ApiUtils.apiRun(3, () -> webSettings.setUserAgentString(config.getUserAgent()));
|
||||
|
||||
ApiUtils.apiRun(3, () -> webSettings.setBuiltInZoomControls(config.getAllowZoomControls()));
|
||||
ApiUtils.apiRun(11, () -> webSettings.setDisplayZoomControls(config.getDisplayZoomControls()));
|
||||
}
|
||||
|
||||
public void loadConfig(Context context, int configResource) {
|
||||
|
@ -4,16 +4,18 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
|
||||
import org.eu.spacc.spaccdotweb.android.utils.ApiUtils;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class WebViewClient extends android.webkit.WebViewClient {
|
||||
public class SpaccWebViewClient extends WebViewClient {
|
||||
private final Context context;
|
||||
|
||||
public WebViewClient(Context context) {
|
||||
public SpaccWebViewClient(Context context) {
|
||||
super();
|
||||
this.context = context;
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
package org.eu.spacc.spaccdotweb.android.webview;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.webkit.ValueCallback;
|
||||
import android.webkit.WebView;
|
||||
|
||||
public class WebChromeClient extends android.webkit.WebChromeClient {
|
||||
private static final int INPUT_FILE_REQUEST_CODE = 1;
|
||||
|
||||
private final Context context;
|
||||
|
||||
public WebChromeClient(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<Uri[]> 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;
|
||||
}
|
||||
}
|
@ -1,9 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<config>
|
||||
<boolean name="allow_javascript">true</boolean>
|
||||
<boolean name="allow_storage">true</boolean>
|
||||
|
||||
<AppIndex name="app_index">local</AppIndex> <!-- local or remote -->
|
||||
<string name="local_index">index.html</string>
|
||||
<string name="remote_index">https://example.com</string>
|
||||
<!-- <string name="standard_font_family"></string> -->
|
||||
<!-- <string name="user_agent"></string> -->
|
||||
</config>
|
@ -1,4 +1,4 @@
|
||||
plugins {
|
||||
id 'com.android.application' version '7.3.1' apply false
|
||||
id 'com.android.library' version '7.3.1' apply false
|
||||
id 'com.android.application' version '7.4.2' apply false
|
||||
id 'com.android.library' version '7.4.2' apply false
|
||||
}
|
||||
|
@ -5,6 +5,10 @@ pluginManagement {
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0'
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
|
Reference in New Issue
Block a user