#Region Activity Attributes
#FullScreen: True
#IncludeTitle: False
#End Region
Sub Process_Globals
Private ActivityParent As JavaObject
Private xui As XUI
Private strUrl As String
Private cc As ContentChooser
Private OldIntent As Intent
Dim rp As RuntimePermissions
Dim NoPause As Boolean
Private Bundle As JavaObject
Private Native As JavaObject
End Sub
Sub Globals
'These global variables will be redeclared each time the activity is created.
'These variables can only be accessed from this module.
Private IME As IME
Dim r As Reflector
Private WebView1 As WebView
Private ActivityParent As JavaObject
Private Native As JavaObject
End Sub
Public Sub Initialize
' B4XPages.GetManager.LogEvents = True
End Sub
Sub Activity_Create(FirstTime As Boolean)
Activity_WindowFocusChanged(True)
Dim lv As LayoutValues = GetRealSize
Dim jo As JavaObject = Activity
jo.RunMethod("setBottom", Array(lv.Height))
jo.RunMethod("setRight", Array(lv.Width))
Activity.Height =100%y' lv.Height
Activity.Width = 100%x'lv.Width
IME.Initialize("ime")
Activity.LoadLayout("lyt_webview")
IME.AddHeightChangedEvent
'Dim client As JavaObject
'client.InitializeNewInstance(Application.PackageName & ".hnlaporan$MyChromeClient", Null)
'Dim jo As JavaObject = WebView1
'jo.RunMethod("setWebChromeClient", Array(client))
Native = Me
Native.RunMethod("Initialize", Null)
'Native.RunMethod("Initialize", Array (WebView1)) ' Initialize and pass context
If Bundle.IsInitialized And Starter.bukawebnew=False Then
Dim jo As JavaObject = WebView1
jo.RunMethod("restoreState", Array(Bundle))
End If
Dim jo As JavaObject = Activity
jo.RunMethodJO("getContext", Null).RunMethodJO("getWindow", Null).RunMethod("setSoftInputMode", _
Array As Object(0x20))
ActivityParent = jo.RunMethodJO("getParent", Null)
'Dim r As Reflector
r.Target = WebView1
r.Target = r.RunMethod("getSettings")
r.RunMethod2("setBuiltInZoomControls", True, "java.lang.boolean")
r.RunMethod2("setDisplayZoomControls", False, "java.lang.boolean")
SetupJavaScriptInterface
SetupDownloadListener
SetupWebChromeClient
' SetupWebViewClient
SetupWebViewSettings(WebView1)
'WebView1.LoadUrl("https://dapurhn.swk-apps.com/laporan/")
BukaWeb("https://dapurhn.swk-apps.com/laporan/")
End Sub
Sub IME1_HeightChanged (NewHeight As Int, OldHeight As Int)
CallSubDelayed(Me, "AfterChange")
End Sub
Sub AfterChange
r.Target = WebView1
r.Target = r.RunMethod("getSettings")
r.RunMethod2("setBuiltInZoomControls", True, "java.lang.boolean")
r.RunMethod2("setDisplayZoomControls", False, "java.lang.boolean")
Dim ajo As Panel = Activity
Dim width As Int = ActivityParent.RunMethod("getMeasuredWidth", Null)
Dim height As Int = ActivityParent.RunMethod("getMeasuredHeight", Null)
If width = 0 Or height = 0 Then Return
ajo.Width = width 'update the "activity" width and height
ajo.Height = height
WebView1.Width = width
WebView1.Height = height
End Sub
Sub IME_HeightChanged (NewHeight As Int, OldHeight As Int)
'CallSubDelayed(Me, "AfterChange")
'WebView1.Height=WebView1.Top-NewHeight
CallSubDelayed(Me, "AfterChange")
End Sub
Sub BukaWeb(xStr As String)
'If Bundle.IsInitialized And Starter.bukawebnew=False Then
' Dim jo As JavaObject = WebView1
' jo.RunMethod("restoreState", Array(Bundle))
'Else
ProgressDialogShow("Loading data ...")
strUrl = xStr
'WebView1.LoadUrl(strUrl)
Dim job2 As HttpJob
job2.Initialize("Job2", Me)
job2.Download(strUrl)
wait for (job2) JobDone(job2 As HttpJob)
If job2.Success = True Then
Select job2.JobName
Case "Job2"
WebView1.JavaScriptEnabled=True
WebView1.LoadUrl(strUrl)
'show the downloaded image
End Select
Else
Msgbox2Async(job2.ErrorMessage , "Error","Stop","","",LoadBitmap(File.DirAssets,"Warning2.png"),True)
Wait For Msgbox_Result (Result As Int)
Activity.Finish
End If
job2.Release
ProgressDialogHide
Starter.bukawebnew=False
'End If
End Sub
Sub JobDone (Job As HttpJob)
End Sub
Sub WebView1_OverrideUrl(Url As String) As Boolean
'ToastMessageShow(Url,False)
Log(Url)
If Url.EndsWith(".pdf") Then
DownloadFile(Url)
Return True
else If Url.EndsWith("tutup/") Then
Activity.Finish
Else
Return False
End If
End Sub
Sub GetRealSize As LayoutValues
Dim lv As LayoutValues
Dim p As Phone
If p.SdkVersion >= 17 Then
Dim ctxt As JavaObject
ctxt.InitializeContext
Dim display As JavaObject = ctxt.RunMethodJO("getSystemService", Array("window")).RunMethod("getDefaultDisplay", Null)
Dim point As JavaObject
point.InitializeNewInstance("android.graphics.Point", Null)
display.RunMethod("getRealSize", Array(point))
lv.Width = point.GetField("x")
lv.Height = point.GetField("y")
Else
lv.Width = 100%x
lv.Height = 100%y
End If
lv.Scale = 100dip / 100
Return lv
End Sub
Sub Activity_WindowFocusChanged(HasFocus As Boolean)
If HasFocus Then
Try
Dim jo As JavaObject = Activity
Sleep(300)
jo.RunMethod("setSystemUiVisibility", Array As Object(5894)) '3846 - non-sticky
Catch
'Log(LastException) 'This can cause another error
End Try 'ignore
End If
End Sub
Sub DownloadFile (link As String)
strUrl=link
Dim myPath As String
myPath =rp.GetSafeDirDefaultExternal("") & "/"
Dim j As HttpJob
j.Initialize("", Me)
j.Download(link)
Wait For (j) JobDone(j As HttpJob)
If j.Success Then
Dim FileName As String = "laporan.xlsx"
Dim out As OutputStream = File.OpenOutput(myPath,FileName,False)
File.Copy2(j.GetInputStream, out)
out.Close
File.Copy(myPath, FileName, Starter.Provider.SharedFolder, FileName)
Dim in As Intent
in.Initialize(in.ACTION_VIEW, "")
Starter.Provider.SetFileUriAsIntentData(in, FileName)
in.SetComponent("android/com.android.internal.app.ResolverActivity")
in.SetType("Application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
StartActivity(in)
Else
MsgboxAsync(j.ErrorMessage, "Error")
End If
j.Release
End Sub
Sub Activity_Resume
AfterChange
End Sub
Sub Activity_Pause (UserClosed As Boolean)
'If UserClosed Then
' StateManager.ResetState("viewhtml")
'Else
' StateManager.SaveState(Activity, "viewhtml")
'End If
'StateManager.SaveSettings
Bundle.InitializeNewInstance("android.os.Bundle", Null)
Dim jo As JavaObject = WebView1
jo.RunMethod("saveState", Array(Bundle))
End Sub
Sub ShowFile_Chooser (FilePathCallback As Object, FileChooserParams As Object)
cc.Initialize("CC")
cc.Show("*/*", "Choose File")
Wait For CC_Result (Success As Boolean, Dir As String, FileName As String)
Dim jo As JavaObject = Me
If Success Then
Dim namafile As String = GetFileInfoByIndex("_display_name", FileName)
'Log(FileName)
File.Copy(Dir, FileName, Starter.Provider.SharedFolder, namafile)
jo.RunMethod("SendResult", Array(Starter.Provider.GetFileUri(namafile),FilePathCallback))
Else
jo.RunMethod("SendResult", Array(Null, FilePathCallback))
End If
End Sub
Sub GetFileInfoByIndex(column As String, uri As String) As String
Dim results As String
Dim Cur As Cursor
Dim Uri1 As Uri
Dim cr As ContentResolver
cr.Initialize("")
'if viewing by gallery
If uri.StartsWith("content://media/") Then
Dim i As Int = uri.LastIndexOf("/")
Dim id As String = uri.SubString(i + 1)
Uri1.Parse(uri)
Cur = cr.Query(Uri1, Null, "_id = ?", Array As String(id), Null)
Cur.Position = 0
If Cur.RowCount <> 0 Then
For i = 0 To Cur.ColumnCount - 1
If Cur.GetColumnName(i) <> Null Then
If Cur.GetColumnName(i) = column Then
results = Cur.GetString2(i)
Exit
End If
End If
Next
End If
Else
Uri1.Parse(uri)
Cur = cr.Query(Uri1, Null, Null, Null, Null)
Cur.Position = 0
If Cur.RowCount <> 0 Then
For i = 0 To Cur.ColumnCount - 1
If Cur.GetColumnName(i) <> Null Then
If Cur.GetColumnName(i) = column Then
results = Cur.GetString2(i)
Exit
End If
End If
Next
End If
End If
Cur.Close
Return results
End Sub
Private Sub IsRelevantIntent(in As Intent) As Boolean
If in.IsInitialized And in <> OldIntent And in.Action = in.ACTION_SEND Then
OldIntent = in
Return True
End If
Return False
End Sub
Sub SetupJavaScriptInterface
Log(">>>>>>>> SetupJavaScriptInterface")
Native.RunMethod("AddJavaScriptInterface", Array ("B4A")) ' Add JavaScript interface
End Sub
Sub SetupWebChromeClient
Log(">>>>>>>> SetupWebChromeClient")
Dim WebChromeClient As JavaObject
WebChromeClient.InitializeNewInstance(Application.PackageName & ".b4xmainpage$MyWebChromeClient", Null)
Dim wvjo As JavaObject = WebView1
wvjo.RunMethod("setWebChromeClient", Array(WebChromeClient))
End Sub
Sub SetupWebViewClient
Log(">>>>>>>> SetupWebViewClient")
Dim WebViewClient As JavaObject
WebViewClient.InitializeNewInstance(Application.PackageName & ".b4xmainpage$MyWebViewClient", Null)
Dim wvjo As JavaObject = WebView1
wvjo.RunMethod("setWebViewClient", Array(WebViewClient))
End Sub
'Download Listener
Sub SetupDownloadListener
Log(">>>>>>>> SetupDownloadListener")
Dim dl As JavaObject
dl.InitializeNewInstance(Application.PackageName & ".b4xmainpage$MyDownloadListener", Null)
dl.RunMethod("set", Array(WebView1))
End Sub
Sub DownloadListener_Event (MethodName As String, Args() As Object) As Object ' What is this ???
Log("DownloadListener = " & MethodName)
Return Null
End Sub
Sub onDownloadStart (Url As String, UserAgent As String, ContentDisposition As String, ContentType As String, ContentLength As Long)
LogColor("Url: " & Url, xui.Color_Green)
LogColor("UserAgent: " & UserAgent, xui.Color_Green)
LogColor("ContentDisposition: " & ContentDisposition, xui.Color_Green)
LogColor("ContentType: " & ContentType, xui.Color_Green)
LogColor("ContentLength: " & ContentLength & " Bytes", xui.Color_Green)
'//////////// TRY TO GET FILE NAME FROM JS /////////////////
''''''' Dim js As String = $"
''''''' // var element = document.getElementById("a");
''''''' //B4A.CallSub('Get_FileName', true, element.download);
''''''' //B4A.CallSub('Get_FileName', true, link.download);
''''''' //B4A.CallSub('Get_FileName', false, document.documentElement.outerHTML);
''''''' B4A.CallSub('Get_FileName', false, JSON.stringify(document.getElementsByTagName('a')));
''''''' "$
''''''' Native.RunMethod("ExecuteJavaScript", Array (js))
''''''' Wait For Get_FileName (fn As String)
''''''' Log("FILE NAME: " & fn)
'//////////////////////////////////////////////////////////
' Download file
Dim Dir As String = File.DirInternal
Dim FileName As String = "box.stl" ' For now we set the file name manually. Content-Disposition header should return it, but always is a void string
Wait For (DownloadAndSaveFile(Url, Dir, FileName)) Complete (Success As Boolean)
Log("RETURNED FROM DownloadAndSaveFile. Success: " & Success)
DoIntent(Dir, FileName , ContentType)
' If Success Then ' Launch default app
' 'ToastMessageShow("Successfully downloaded file: " & FileName,True)
' Else
' ToastMessageShow("Error Downloading file: " & FileName, True)
' End If
End Sub
Sub DoIntent(Dir As String, FileName As String, ContentType As String)
Dim DestDir As String = File.Combine(File.DirRootExternal, "Download") ' Add WRITE_EXTERNAL_STORAGE
Wait For (File.CopyAsync(Dir, FileName, DestDir, FileName)) Complete (Success As Boolean)
If Success = False Then
Log("ERROR WHILE COPY FILE FROM DIR.INTERNAL TO DIR.ROOT.EXTERNAL: " & LastException)
Return
End If
' Similar to content:///storage/emulated/0/Download/2_lug_2024_07-24-40.txt
Dim i As Intent
' i.Initialize(i.ACTION_VIEW, "")
Dim URI As String = "content://" & File.Combine(DestDir, FileName) 'xui.FileUri(Dir, FileName) ' Should be content:///data/user/0/b4a.example/files/box.stl
Log("INTENT URI: " & URI)
i.Initialize(i.ACTION_VIEW, URI)
' i.SetType(ContentType)
i.SetType("*/*")
StartActivity(i)
End Sub
Sub DownloadAndSaveFile (Url As String, Dir As String, FileName As String) As ResumableSub
If File.Exists(Dir, FileName) Then
File.Delete(Dir, FileName)
Log("DELETED: " & File.Combine(Dir, FileName)) ' We delete a file before download it if exists. We can just overwrite it.
End If
Dim Success As Boolean = False
If (Url.StartsWith("http:")) Or (Url.StartsWith("https:")) Then ' Download file with httppjob
If Url.StartsWith("https:") Then
Log("[HTTPS] URL: " & Url)
Else
Log("[HTTP] URL: " & Url)
End If
Try
Dim j As HttpJob
j.Initialize("", Me)
LogColor("Start download. URL: " & Url, xui.Color_Green)
j.Download(Url)
' We can set some request headers ...
' j.GetRequest.SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0")
' j.GetRequest.SetHeader("Access-Control-Allow-Origin", "true")
' j.GetRequest.SetHeader ("ContentType", "text/plain")
' j.GetRequest.SetHeader ("ContentType", "application/octet-stream")
' j.GetRequest.SetHeader ("Host", "exampleproject.com")
' j.GetRequest.SetHeader ("Connection", "close")
Wait For (j) JobDone(j As HttpJob)
Dim map As Map = j.Response.GetHeaders ' ... and get a list of headers from a response
For i = 0 To map.Size - 1
Log("HEADER: " & map.GetKeyAt(i) & ": " & map.GetValueAt(i))
Next
Log(" ")
Log("Success: " & j.Success)
Log("StatusCode: " & j.Response.StatusCode)
Log("ErrorResponse: " & j.Response.ErrorResponse)
Log("ContentType: " & j.Response.ContentType)
Log("ContentLength: " & j.Response.ContentLength)
Log("ContentEncoding: " & j.Response.ContentEncoding)
Log(" ")
If j.Success And j.Response.ContentLength > 0 Then
Dim out As OutputStream = File.OpenOutput(Dir, FileName, False) '"benchy.gcode"
File.Copy2(j.GetInputStream, out)
out.Close ' <------ Very important
Dim Size As Long = File.Size(Dir, FileName)
Log("FILE SAVED: " & FileName & TAB & " Size: " & Size & " Bytes")
ToastMessageShow("FILE SAVED: " & FileName, False)
Dim s As String = File.ReadString(Dir, FileName)
' Log(s)
Log(s.SubString2(0, Min(100, Size))) ' Just log some content
Log("... and more ...")
Success = True
Else
Log("ERROR: " & j.ErrorMessage)
ToastMessageShow("FILE DOWNLOADED FAILED: " & j.ErrorMessage, True)
End If
Catch
ToastMessageShow("FILE DOWNLOADED FAILED: " & LastException.Message, True)
End Try
j.Release
Else If (Url.StartsWith("blob:")) Then
Log("[BLOB] URL: " & Url)
' THIS CODE WAS PLACED IN THE JAVA CODE, SO IT SIMPLIFY THE WORK AND JUST DO IT IN BACKGROUND. To get data bytes, just wait it with 'Wait For blob_data (Bytes() As Byte)' without worry about this ....
'''''' Dim js As String = Native.RunMethod("getBase64StringFromBlobUrl", Array As String (Url, MimeType))
'''''' Log("RETURNED JS CODE FROM getBase64StringFromBlobUrl: [" & js & "]")
''''''
'''''' If js.Length > 0 Then
'''''' Native.RunMethod("ExecuteJavaScript", Array ("alert('FOUND Blob URL');"))
'''''' Else
'''''' Native.RunMethod("ExecuteJavaScript", Array ("alert('It is not a Blob URL');"))
'''''' End If
''''''
'''''' Native.RunMethod("ExecuteJavaScript", Array (js))
Wait For blob_data (Bytes() As Byte) ' <<<<<<<<<<<<<<<<< Get blob data bytes
Try
File.WriteBytes(Dir, FileName, Bytes)
If File.Exists(Dir, FileName) Then
Dim Size As Long = File.Size(Dir, FileName)
Log("BLOB FILE SAVED: " & FileName & TAB & " Size: " & Size & " Bytes")
ToastMessageShow("BLOB FILE SAVED: " & FileName, False)
Dim s As String = File.ReadString(Dir, FileName)
' Log(s)
Log(s.SubString2(0, Min(100, Size))) ' Just log some content ' Just log some content
Log("... and more ...")
Success = True
Else
Log("ERROR: Something went wrong while save the 'blob' file.")
ToastMessageShow("DOWNLOAD FAILED", True)
End If
Catch
Log("ERROR: Something went wrong while save the 'blob' file: " & FileName & " " & LastException.Message)
ToastMessageShow("DOWNLOAD FAILED: " & LastException.Message, True)
End Try
Else
Log("UNKNOW FILE DATA SCHEME. SUPPORTED: http, https, blob")
End If
Log(" ") : Log("LISTING FILES ON DIR.INTERNAL:" & Dir)
Wait For (File.ListFilesAsync(Dir)) Complete (succ As Boolean, Files As List)
If succ Then
For Each f As String In Files
If File.IsDirectory(Dir, f) Then
Log("DIR/: " & f)
Else
Log("FILE: " & f & TAB & " Size: " & File.Size(Dir,f) & " Bytes")
End If
Next
Else
Log("Error listing files in " & Dir)
End If
Log(" ")
Return Success
End Sub
#if JAVA
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.keywords.Common;
import android.webkit.*;
import android.webkit.WebChromeClient.*;
import android.webkit.JavascriptInterface;
import android.webkit.ValueCallback;
import android.webkit.WebView;
import android.net.*;
import java.io.IOException;
import android.os.Build;
import android.widget.Toast;
import android.util.Base64;
import java.util.Date;
import java.text.DateFormat;
import java.io.File;
import java.io.FileOutputStream;
import android.os.Environment;
import android.os.Handler;
import android.graphics.Bitmap; // for favicon
import android.webkit.URLUtil;
import android.content.Intent;
import android.app.PendingIntent;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
private static BA parent;
private static WebView webView;
private static String fileMimeType;
private static String iFaceName;
/////////////////////////////////////////////
public void Initialize(BA ba, final WebView wv) {
parent = ba;
webView = wv;
BA.Log("Initialize => Parent: " + parent);
}
public void SendResult(Uri uri, ValueCallback<Uri[]> filePathCallback) { // This is not used ???
if (uri != null)
filePathCallback.onReceiveValue(new Uri[] {uri});
else
filePathCallback.onReceiveValue(null);
}
public static class MyWebChromeClient extends WebChromeClient {
@Override
public boolean onConsoleMessage(final ConsoleMessage consoleMessage1) {
String logMessage = consoleMessage1.messageLevel().toString() + ": " + consoleMessage1.message() + " In " +
consoleMessage1.sourceId() + " (Line: " + consoleMessage1.lineNumber() + ")";
BA.Log("CONSOLE " + logMessage);
return true;
}
@Override
public boolean onJsAlert(WebView view, String url, String message, final android.webkit.JsResult result) {
//BA.Log("onJsAlert: " + message + " From Url: " + url);
//Toast.makeText(parent.context, message, 3000).show();
//result.confirm(); // Dismiss alert box
//return true;
return false;
};
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) { // This is not used ???
BA.Log("onShowFileChooser");
parent.raiseEventFromUI(this, "showfile_chooser", filePathCallback, fileChooserParams, fileChooserParams.getAcceptTypes());
return true;
}
@Override
public void onProgressChanged(WebView webView, int NewProgress) {
//parent.raiseEvent(this, eventName + "_progresschanged", new Object[] { NewProgress });
parent.raiseEventFromUI(this, "progress_changed", new Object[] { NewProgress });
}
}
public static class MyWebViewClient extends WebViewClient {
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
}
@Override
public void onPageFinished(WebView webView, String url) {
//String s = "WebViewClient onPageFinished initialized " + url;
//view.loadUrl("javascript: alert(" + s + ");");
//ExecuteJavaScript("alert('" + s + "');"); // Run script on every page after load
}
//...
}
public static class MyDownloadListener implements android.webkit.DownloadListener {
public void set(android.webkit.WebView wv) {
BA.Log("setDownloadListener");
wv.setDownloadListener(this);
BA.Log("DownloadListener is done");
}
public void onDownloadStart(String url, String userAgent, String contentDisposition, String contentType, long contentLength) {
BA.Log("contentDisposition: [" + contentDisposition + "]");
String js = getBase64StringFromBlobUrl(url, contentType);
////////////BA.Log("RETURNED JS CODE FROM getBase64StringFromBlobUrl: [" + js + "]");
if(contentDisposition.length() > 0 && contentDisposition.contains("filename=")) { // ContentDisposition always return a void string
BA.Log("TRY TO GET FILE NAME");
String fname = contentDisposition.replaceFirst("(?i)^.*filename=\"?([^\"]+)\"?.*$", "$1");
BA.Log("FILE NAME: [" + fname + "]");
}
if (js.length() > 0) {
//ExecuteJavaScript("alert('FOUND Blob URL');");
ExecuteJavaScript("console.log('FOUND Blob URL');");
} else {
//ExecuteJavaScript("alert('It is not a Blob URL');");
ExecuteJavaScript("console.log('It is not a Blob URL');");
}
ExecuteJavaScript(js);
parent.raiseEventFromUI(this, "ondownloadstart", url, userAgent, contentDisposition, contentType, contentLength); // Notify to B4A the download start
}
}
public static String getBase64StringFromBlobUrl(String blobUrl, String mimeType) { // (1)
fileMimeType = mimeType;
////////////BA.Log("getBase64StringFromBlobUrl ==> URL: " + blobUrl + " MimeType: " + mimeType);
if(blobUrl.startsWith("blob")) { // If URL is a blob do a GET request to get the Blob and to just return it's data bytes. Base64 is involved, check next the code.
BA.Log("Send GET XMLHttpRequest: " + blobUrl + " MimeType: " + mimeType);
return "var xhr = new XMLHttpRequest();" +
" xhr.open('GET', '" + blobUrl + "', true);" +
//" xhr.withCredentials = true;" +
" xhr.setRequestHeader('Access-Control-Allow-Origin', true);" +
" xhr.setRequestHeader('Content-type','" + mimeType + "; charset=UTF-8');" +
" xhr.responseType = 'blob';" +
" xhr.onload = function(e) {" +
" if (this.status == 200) {" +
" var blobFile = this.response;" +
" var reader = new FileReader();" +
" reader.readAsDataURL(blobFile);" +
" reader.onloadend = function() {" +
" var base64data = reader.result;" +
" " + iFaceName + ".getBase64FromBlobData(base64data);" + // Here we call a JavascriptInterface function, in this case BA.getBase64FromBlobData(base64data);
" }" +
" }" +
" };" +
" xhr.send();" + // Send a request
" xhr.onreadystatechange = () => {" +
" if (xhr.readyState === xhr.HEADERS_RECEIVED) {" +
" console.log('>>>>>>>> RECEIVED HEADERS <<<<<<<<');" +
" const headers = xhr.getAllResponseHeaders();" +
" console.log('Headers: ' + JSON.stringify(headers, null, 3));" +
" const arr = headers.trim().split((\"\\r\\n\"));" +
" console.log('Header size: ' + arr.length);" +
" const headerArray = [];" +
//" const headerMap = {};" +
" arr.forEach((line) => {" +
//" const parts = line.split(\": \");" +
//" const header = parts.shift();" +
//" const value = parts.join(\": \");" +
//" headerMap[header] = value;" +
" headerArray.push(line);" +
" });" +
" var idx = 0;" +
//" for (const header of headerArray) {" + // both works
" headerArray.forEach((header) => {" +
" idx++;" +
" console.log('[HEADER ' + idx + '] => ' + header);" +
// " }" +
" });" +
//" const contentLength = headerMap[\"content-length\"];" +
//" console.log('ContentLength: ' + contentLength + \" Bytes\");" +
//" const contentType = headerMap[\"content-type\"];" +
//" console.log('ContentType: ' + contentType);" +
" }" +
"};"
;
}
return "";
}
// Javascript interface
public static final void AddJavaScriptInterface(final String InterfaceName) {
iFaceName = InterfaceName;
final class MyJavaScriptInterface {
private int TaskId = 0;
//private String fileMimeType;
public MyJavaScriptInterface(BA ba) {
parent = ba;
}
////////// FOR BLOBS /////////
@JavascriptInterface
public void getBase64FromBlobData(String base64Data) throws IOException { // (2)
////////////BA.Log("getBase64FromBlobData called by JS base64Data: " + base64Data);
convertBase64StringToFileAndStoreIt(base64Data);
}
private void convertBase64StringToFileAndStoreIt(String base64Data) throws IOException { // (3)
////////////BA.Log("convertBase64StringToFileAndStoreIt base64Data: " + base64Data);
//final int notificationId = 1;
String currentDateTime = DateFormat.getDateTimeInstance().format(new Date());
String newTime = currentDateTime.replaceFirst(", ","_").replaceAll(" ","_").replaceAll(":","-");
MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
String extension = mimeTypeMap.getExtensionFromMimeType(fileMimeType);
//String type = mimeTypeMap.getMimeTypeFromExtension(extension);
final File dlPath = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/" + newTime + "." + extension);
String regex = "^data:" + fileMimeType + ";base64,";
byte[] fileBytes = Base64.decode(base64Data.replaceFirst(regex, ""), 0);
BA.Log("MimeType ==> " + fileMimeType);
BA.Log("Extension ==> [" + extension + "]");
BA.Log("Path ==> " + dlPath);
BA.Log("regex ==> " + regex);
parent.raiseEventFromUI(this, "OpenExcelFileSimple", dlPath);
// Debug base64 string
if (extension.equals("bin") == false) {
String s = base64Data.replaceFirst(regex, "");
BA.Log("BASE64 DATA: " + s.substring(0, Math.min(30, s.length())) + " ... and more ...");
}
parent.raiseEvent(this, "blob_data", new Object[] { fileBytes }); // Return back the blob data to B4A
}
@JavascriptInterface
public String CallSub(final String SubName, final boolean pCallUIThread) {
String subNameLowerCase = SubName.toLowerCase(BA.cul);
if (parent.subExists(subNameLowerCase)) {
if (pCallUIThread) {
return (String) parent.raiseEventFromDifferentThread(this, this, TaskId++, subNameLowerCase, false, new Object[0]);
} else {
return (String) parent.raiseEvent(this, subNameLowerCase, new Object[0]);
}
} else {
return "JavascriptInterface error: " + SubName + " sub does not exist";
}
}
@JavascriptInterface
public String CallSub(final String SubName, final boolean CallUIThread, final String p1) {
String subNameLowerCase = SubName.toLowerCase(BA.cul);
if (parent.subExists(subNameLowerCase)) {
//BA.Log("Sub exist: '" + SubName + "'. Call it and send '" + param1 + "'");
Object[] parameters = { p1 };
if (CallUIThread) {
return (String) parent.raiseEventFromDifferentThread(this, this, TaskId++, subNameLowerCase, false, parameters);
} else {
return (String) parent.raiseEvent(this, subNameLowerCase, parameters);
}
} else {
return "JavaScriptInterface: " + SubName + " sub does not exist";
}
}
}
BA.Log("parent: " + parent);
webView.addJavascriptInterface(new MyJavaScriptInterface(parent), iFaceName);
BA.Log("Javascript Interface '" + iFaceName + "' Successfully Initialized");
}
public static final void ExecuteJavaScript(final String js) {
BA.Log(">>>>>>>> ExecuteJavaScript: " + js);
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.evaluateJavascript(js, null);
BA.Log(">>>>>>>> js1: " + js);
} else {
webView.loadUrl("javascript:" + js);
BA.Log(">>>>>>>> js2: " + js);
}
} catch (Exception e) {
BA.Log("ExecuteJavascript Exception: " + e);
}
}
private static String getFileSizeMegaBytes(File file) {
return (double) file.length() / (1024 * 1024) + " MB";
}
private static String getFileSizeKiloBytes(File file) {
return (double) file.length() / 1024 + " KB";
}
private static String getFileSizeBytes(File file) {
return file.length() + " Bytes";
}
#End If
'--
Sub SetupWebViewSettings(wv As WebView)
Log(">>>>>>>> SetupWebViewSettings")
SetWebViewSetting (wv, "AllowFileAccess", True) : Log("AllowFileAccess: " & GetWebViewSetting (wv, "AllowFileAccess"))
SetWebViewSetting (wv, "AllowContentAccess", True) : Log("AllowContentAccess: " & GetWebViewSetting (wv, "AllowContentAccess"))
SetWebViewSetting (wv, "DomStorageEnabled", True) : Log("DomStorageEnabled: " & GetWebViewSetting (wv, "DomStorageEnabled"))
' Used here but it is advised to always set to False (if not used) to avoid vulnerability
' Fix load additional files in via js due to CORS blocking it Access to XMLHttpRequest from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-untrusted, https.
SetWebViewSetting (wv, "AllowUniversalAccessFromFileURLs", True) : Log("AllowUniversalAccessFromFileURLs: " & GetWebViewSetting (wv, "AllowUniversalAccessFromFileURLs"))
SetWebViewSetting (wv, "AllowFileAccessFromFileURLs", True) : Log("AllowFileAccessFromFileURLs: " & GetWebViewSetting (wv, "AllowFileAccessFromFileURLs"))
SetWebViewSetting (wv, "UseWideViewPort", True) : Log("UseWideViewPort: " & GetWebViewSetting (wv, "UseWideViewPort"))
SetWebViewSetting (wv, "JavaScriptCanOpenWindowsAutomatically", True) : Log("JavaScriptCanOpenWindowsAutomatically: " & GetWebViewSetting (wv, "JavaScriptCanOpenWindowsAutomatically"))
SetWebViewSetting (wv, "LoadWithOverviewMode", True) : Log("LoadWithOverviewMode: " & GetWebViewSetting (wv, "LoadWithOverviewMode"))
SetWebViewSetting (wv, "DisplayZoomControls", False) : Log("DisplayZoomControls: " & GetWebViewSetting (wv, "DisplayZoomControls"))
' LOAD_DEFAULT = -1 Default cache usage mode. If the navigation type doesn't impose any specific behavior, use cached resources when they are available and not expired, otherwise load resources from the network.
' LOAD_NORMAL = 0 Normal cache usage mode. Deprecated in API level 17
' LOAD_CACHE_ELSE_NETWORK = 1 Use cached resources when they are available, even if they have expired. Otherwise load resources from the network
' LOAD_NO_CACHE = 2 Don't use the cache, load from the network
' LOAD_CACHE_ONLY =3 Don't use the network, load from the cache
SetWebViewSetting (wv, "CacheMode", 2) : Log("CacheMode: " & GetWebViewSetting (wv, "CacheMode"))
' FORCE_DARK_OFF = 0 Disable force dark, irrespective of the force dark mode of the WebView parent. In this mode, WebView content will always be rendered as-is, regardless of whether native views are being automatically darkened.
' FORCE_DARK_AUTO = 1 Enable force dark dependent on the state of the WebView parent view. If the WebView parent view is being automatically force darkened (see: View.setForceDarkAllowed(boolean)), then WebView content will be rendered so as to emulate a dark theme. WebViews that are not attached to the view hierarchy will not be inverted
' FORCE_DARK_ON = 2 Unconditionally enable force dark. In this mode WebView content will always be rendered so as to emulate a dark theme.
SetWebViewSetting (wv, "ForceDark", 2) : Log("ForceDark: " & GetWebViewSetting (wv, "ForceDark")) ' Always remain FORCE_DARK_AUTO on SDK 33
SetWebViewSetting (wv, "LoadsImagesAutomatically", True) : Log("LoadsImagesAutomatically: " & GetWebViewSetting (wv, "LoadsImagesAutomatically"))
SetWebViewSetting (wv, "BlockNetworkImage", False) : Log("BlockNetworkImage: " & GetWebViewSetting (wv, "BlockNetworkImage"))
SetWebViewSetting (wv, "BlockNetworkLoads", False) : Log("BlockNetworkLoads: " & GetWebViewSetting (wv, "BlockNetworkLoads"))
SetWebViewSetting (wv, "DefaultTextEncodingName", "utf-8") : Log("DefaultTextEncodingName: " & GetWebViewSetting (wv, "DefaultTextEncodingName"))
Log("UserAgentString: " & GetWebViewSetting (wv, "UserAgentString"))
' SetWebViewSetting (wv, "UserAgentString", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246")
' Log("UserAgentString: " & GetWebViewSetting (wv, "UserAgentString"))
End Sub
Public Sub SetWebViewSetting (wv As WebView, Method As String, Value As Object)
Dim sType As String
If Value Is Boolean Then
sType = "java.lang.boolean"
Else If Value Is Int Then
sType = "java.lang.int"
Else If Value Is String Then
sType = "java.lang.String"
End If
Dim jo As JavaObject = wv
Dim settings As JavaObject = jo.RunMethod("getSettings", Null)
Dim r As Reflector
r.Target = settings
r.RunMethod2($"set${Method}"$, Value, sType)
End Sub
Public Sub GetWebViewSetting (wv As WebView, Method As String) As Object
Dim jo As JavaObject = wv
Dim settings As JavaObject = jo.RunMethod("getSettings", Null)
Dim r As Reflector
r.Target = settings
Return r.RunMethod($"get${Method}"$)
End Sub