[SpaccWebView.Android] Add file downloads, initial work for other things
This commit is contained in:
parent
b582399d06
commit
04b053cbc2
|
@ -8,7 +8,7 @@
|
|||
<!-- <uses-permission android:name="android.permission.INTERNET" /> -->
|
||||
|
||||
<!-- 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 -->
|
||||
<!-- 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.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
|
|
|
@ -21,10 +21,18 @@
|
|||
<li><a href="http://example.com">http://</a></li>
|
||||
<li><a href="https://example.com">https://</a></li>
|
||||
<li><a href="ftp://example.com">ftp://</a></li>
|
||||
<li><a href="data:text/plain;utf8,Hello World!">data:text/plain</a></li>
|
||||
<li><a href="data:text/html;utf8,<h2>Hello World!">data:text/html</a></li>
|
||||
<li><a href="mailto:example@example.com">mailto:</a></li>
|
||||
</ul>
|
||||
<h3>Files</h3>
|
||||
<p>Upload: <label><input type="file"/></label></p>
|
||||
<p>Download: <a download="test.bin" href="data:text/plain;utf8,Test">Download</a></p>
|
||||
<p>Download:</p>
|
||||
<ul>
|
||||
<li><a download="Hello World.txt" href="data:text/plain;utf8,Hello World!">data:, .txt</a></li>
|
||||
<li><a download="Example.html" href="http://example.com/index.html">http://, .html</a></li>
|
||||
<li><a download="Example.html" href="https://example.com/index.html">https://, .html</a></li>
|
||||
<li><a href="https://hlb0.octt.eu.org/Drive/Misc/onestop.mid">https://, .mid</a></li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
|
@ -1,7 +1,9 @@
|
|||
package com.example.spaccwebviewapplication;
|
||||
|
||||
import android.os.Bundle;
|
||||
import org.eu.spacc.spaccdotweb.android.*;
|
||||
|
||||
import org.eu.spacc.spaccdotweb.android.helpers.DataMoveHelper;
|
||||
import org.eu.spacc.spaccdotweb.android.SpaccWebViewActivity;
|
||||
|
||||
public class MainActivity extends SpaccWebViewActivity {
|
||||
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
package org.eu.spacc.spaccdotweb.android;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
public class ApiUtils {
|
||||
|
||||
public static void apiRun(int apiLevel, Runnable action) {
|
||||
if (Build.VERSION.SDK_INT >= apiLevel) {
|
||||
action.run();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package org.eu.spacc.spaccdotweb.android;
|
||||
|
||||
public class Constants {
|
||||
//public enum ActivityCodes { DOWNLOAD_FILE, UPLOAD_FILE }
|
||||
public enum AppIndex { LOCAL, REMOTE }
|
||||
public enum DataLocation { INTERNAL, EXTERNAL }
|
||||
}
|
||||
|
|
|
@ -2,11 +2,23 @@ package org.eu.spacc.spaccdotweb.android;
|
|||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import org.eu.spacc.spaccdotweb.android.webview.SpaccWebView;
|
||||
import java.io.File;
|
||||
|
||||
public class SpaccWebViewActivity extends Activity {
|
||||
protected SpaccWebView webView;
|
||||
|
||||
// @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()));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (this.webView.canGoBack()) {
|
||||
|
@ -16,6 +28,16 @@ public class SpaccWebViewActivity extends Activity {
|
|||
}
|
||||
}
|
||||
|
||||
// // TODO: Find some way to download to any storage location with DownloadManager, since it doesn't take content:// URIs
|
||||
// private void enqueueDownload(Uri fileUri) {
|
||||
// DownloadDataHolder downloadDataHolder = DownloadDataHolder.getInstance();
|
||||
// FileUtils.startFileDownload(this,
|
||||
// downloadDataHolder.getDownloadUrl(),
|
||||
// downloadDataHolder.getContentDisposition(),
|
||||
// downloadDataHolder.getUserAgent(),
|
||||
// downloadDataHolder.getMimeType());
|
||||
// }
|
||||
|
||||
@SuppressLint("NewApi") // We have our custom implementation
|
||||
@Override
|
||||
public File getDataDir() {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.eu.spacc.spaccdotweb.android;
|
||||
|
||||
import android.app.Application;
|
||||
import org.eu.spacc.spaccdotweb.android.utils.StorageUtils;
|
||||
import java.io.File;
|
||||
|
||||
public class SpaccWebViewApplication extends Application {
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
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<String> 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package org.eu.spacc.spaccdotweb.android;
|
||||
package org.eu.spacc.spaccdotweb.android.helpers;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
|
@ -6,6 +6,11 @@ import android.content.Context;
|
|||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
|
||||
import org.eu.spacc.spaccdotweb.android.Constants;
|
||||
import org.eu.spacc.spaccdotweb.android.utils.StorageUtils;
|
||||
import org.eu.spacc.spaccdotweb.android.utils.FileUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DataMoveHelper {
|
|
@ -1,4 +1,4 @@
|
|||
package org.eu.spacc.spaccdotweb.android;
|
||||
package org.eu.spacc.spaccdotweb.android.helpers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
|
@ -0,0 +1,26 @@
|
|||
package org.eu.spacc.spaccdotweb.android.utils;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
|
||||
public class ApiUtils {
|
||||
|
||||
public static void apiRun(int apiLevel, Runnable action) {
|
||||
if (Build.VERSION.SDK_INT >= apiLevel) {
|
||||
action.run();
|
||||
}
|
||||
}
|
||||
|
||||
public static void openOrShareUrl(Context context, Uri url) {
|
||||
try {
|
||||
// Open the URL externally
|
||||
context.startActivity(new Intent(Intent.ACTION_VIEW, 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.toString()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,12 @@
|
|||
package org.eu.spacc.spaccdotweb.android;
|
||||
package org.eu.spacc.spaccdotweb.android.utils;
|
||||
|
||||
import static android.content.Context.DOWNLOAD_SERVICE;
|
||||
import android.app.DownloadManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.webkit.CookieManager;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
|
@ -60,4 +67,21 @@ public class FileUtils {
|
|||
rootDir.delete();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Handle downloads internally on old Android versions
|
||||
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:")) {
|
||||
// TODO: We should handle downloading data: URIs manually
|
||||
DownloadManager.Request request = new DownloadManager.Request(downloadUrl)
|
||||
//.setDestinationUri(fileUri)
|
||||
.setMimeType(mimeType)
|
||||
.addRequestHeader("User-Agent", userAgent)
|
||||
.addRequestHeader("Content-Disposition", contentDisposition)
|
||||
.addRequestHeader("Cookie", CookieManager.getInstance().getCookie(downloadUrl.toString()));
|
||||
ApiUtils.apiRun(11, () -> request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED));
|
||||
((DownloadManager)context.getSystemService(DOWNLOAD_SERVICE)).enqueue(request);
|
||||
} else {
|
||||
ApiUtils.openOrShareUrl(context, downloadUrl);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package org.eu.spacc.spaccdotweb.android;
|
||||
package org.eu.spacc.spaccdotweb.android.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
|
@ -0,0 +1,46 @@
|
|||
package org.eu.spacc.spaccdotweb.android.webview;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
public class DownloadDataHolder {
|
||||
private static DownloadDataHolder instance;
|
||||
private Uri downloadUrl;
|
||||
private String userAgent;
|
||||
private String contentDisposition;
|
||||
private String mimeType;
|
||||
|
||||
public static synchronized DownloadDataHolder getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new DownloadDataHolder();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void setData(Uri downloadUrl, String userAgent, String contentDisposition, String mimeType, long contentLength) {
|
||||
this.downloadUrl = downloadUrl;
|
||||
this.userAgent = userAgent;
|
||||
this.contentDisposition = contentDisposition;
|
||||
this.mimeType = mimeType;
|
||||
}
|
||||
|
||||
public void clearData() {
|
||||
instance = new DownloadDataHolder();
|
||||
}
|
||||
|
||||
public Uri getDownloadUrl() {
|
||||
return downloadUrl;
|
||||
}
|
||||
|
||||
public String getUserAgent() {
|
||||
return userAgent;
|
||||
}
|
||||
|
||||
public String getContentDisposition() {
|
||||
return contentDisposition;
|
||||
}
|
||||
|
||||
public String getMimeType() {
|
||||
return mimeType;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package org.eu.spacc.spaccdotweb.android.webview;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import org.eu.spacc.spaccdotweb.android.utils.FileUtils;
|
||||
|
||||
public class DownloadListener implements android.webkit.DownloadListener {
|
||||
private final Context context;
|
||||
|
||||
public DownloadListener(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
// TODO: Read file name from download="..." HTML <a> attribute when present
|
||||
// TODO: Implement file destination path picking (requires Android < 5 with SAF Intent)
|
||||
@Override
|
||||
public void onDownloadStart(String downloadUrl, String userAgent, String contentDisposition, String mimeType, long contentLength) {
|
||||
// String[] nameParts = downloadUrl.split("/");
|
||||
// Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT)
|
||||
// .addCategory(Intent.CATEGORY_OPENABLE)
|
||||
// .setType(mimeType)
|
||||
// .putExtra(Intent.EXTRA_TITLE, nameParts[nameParts.length - 1]);
|
||||
// DownloadDataHolder.getInstance().setData(Uri.parse(downloadUrl), userAgent, contentDisposition, mimeType, contentLength);
|
||||
// ((Activity)context).startActivityForResult(intent, Constants.CREATE_FILE_REQUEST_CODE);
|
||||
FileUtils.startFileDownload(context, Uri.parse(downloadUrl), userAgent, contentDisposition, mimeType);
|
||||
}
|
||||
}
|
|
@ -1,18 +1,23 @@
|
|||
package org.eu.spacc.spaccdotweb.android;
|
||||
package org.eu.spacc.spaccdotweb.android.webview;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ContextMenu;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import org.eu.spacc.spaccdotweb.android.utils.ApiUtils;
|
||||
import org.eu.spacc.spaccdotweb.android.Config;
|
||||
|
||||
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));
|
||||
this.setWebViewClient(new WebViewClient(context));
|
||||
this.setWebChromeClient(new WebChromeClient(context));
|
||||
this.setDownloadListener(new DownloadListener(context));
|
||||
|
||||
WebSettings webSettings = this.getSettings();
|
||||
|
||||
|
@ -27,6 +32,12 @@ public class SpaccWebView extends WebView {
|
|||
ApiUtils.apiRun(3, () -> webSettings.setAllowFileAccess(false));
|
||||
}
|
||||
|
||||
// TODO: Implement context menu (long-press on links, images, etc...)
|
||||
// @Override
|
||||
// protected void onCreateContextMenu(ContextMenu menu) {
|
||||
// super.onCreateContextMenu(menu);
|
||||
// }
|
||||
|
||||
public void loadAppIndex() {
|
||||
String url = null;
|
||||
switch (Config.APP_INDEX) {
|
|
@ -1,19 +1,18 @@
|
|||
package org.eu.spacc.spaccdotweb.android;
|
||||
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.WebChromeClient;
|
||||
import android.webkit.WebView;
|
||||
|
||||
public class SpaccWebChromeClient extends WebChromeClient {
|
||||
public class WebChromeClient extends android.webkit.WebChromeClient {
|
||||
private static final int INPUT_FILE_REQUEST_CODE = 1;
|
||||
|
||||
private final Context context;
|
||||
|
||||
public SpaccWebChromeClient(Context context) {
|
||||
public WebChromeClient(Context context) {
|
||||
super();
|
||||
this.context = context;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package org.eu.spacc.spaccdotweb.android.webview;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import org.eu.spacc.spaccdotweb.android.utils.ApiUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class WebViewClient extends android.webkit.WebViewClient {
|
||||
private final Context context;
|
||||
|
||||
public WebViewClient(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 (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...
|
||||
List<String> protocols = Arrays.asList("data", "http", "https", "mailto", "ftp");
|
||||
if (protocols.contains(url.toLowerCase().split(":")[0])) {
|
||||
ApiUtils.openOrShareUrl(context, Uri.parse(url));
|
||||
return true;
|
||||
} else {
|
||||
return super.shouldOverrideUrlLoading(view, url);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
<org.eu.spacc.spaccdotweb.android.SpaccWebView
|
||||
<org.eu.spacc.spaccdotweb.android.webview.SpaccWebView
|
||||
android:id="@+id/webview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
|
Loading…
Reference in New Issue