Jump to content
Yaron

Android, TWebBrowser & Uploading files

Recommended Posts

I use an FMX TWebBrowser component with Android to display a web form.

The form is used to upload an image to a web server, however, after loading the web-page, clicking on the form's "Browse" button does absolutely nothing.

Ideally, I would want Android to trigger the gallery app to select a picture for upload (like it does if I open the same URL with chrome/firefox), any ideas?

Share this post


Link to post

EMBT need to modify the Java code for the WebBrowser class (in source\rtl\androiddex\java\fmx\src\com\embarcadero\firemonkey\webbrowser\WebBrowser.java) so that it uses a descendant of WebChromeClient that implements onShowFileChooser, much like the Java equivalent, here:

 

https://stackoverflow.com/a/36413800/3164070

 

It's possible to do something similar by:

 

Creating your own descendant (in Java), creating a jar for it and importing it 

Patching FMX.WebBrowser.Android to call setWebChromeClient on FWebView for an instance of your descendant

 

Share this post


Link to post

I've managed to come up with something without having to patch FMX.WebBrowser.Android, but it still required Java code. I've put a demo here:

 

  https://github.com/DelphiWorlds/KastriFree/tree/master/Demos/WebBrowserFileChooser

 

Note that it relies on other units in the Kastri Free project:

 

  https://github.com/DelphiWorlds/KastriFree

 

..including the compiled .jar, so you might want to just clone the repo and load the demo from it.

Edited by Dave Nottage
Grammar
  • Like 3

Share this post


Link to post

[ignore this]

Edited by Yaron
oops
  • Like 1

Share this post


Link to post
On 11/25/2019 at 1:04 PM, Dave Nottage said:

I've managed to come up with something without having to patch FMX.WebBrowser.Android, but it still required Java code. I've put a demo here:

 

  https://github.com/DelphiWorlds/KastriFree/tree/master/Demos/WebBrowserFileChooser

 

Note that it relies on other units in the Kastri Free project:

 

  https://github.com/DelphiWorlds/KastriFree

 

..including the compiled .jar, so you might want to just clone the repo and load the demo from it.

 

The demo project seems to work, but when porting the snippet into my own code I'm catching an exception when calling "FWebManager := TWebChromeClientManager.Create(VoucherimWebBrowser);" :

"Error creating FWebManager : java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.ClassLoader java.lang.Class.getClassLoader()' on a null object reference"

 

Other than renaming the variables/components, I used the exact same OnCreate override as in the sample and just added an "FWebManager := nil" when closing the app.

You mentioned including the jar file, but I haven't seen anything in particular in the demo itself, so I'm assuming it's included by one of the units (which I also included in my project).

 

Any ideas?  If needed, I can post the entire source code for this app, it's just a wrapper around a web site.

 

Unrelated, I get a hint while compiling:

[DCC Hint] DW.WebChromeClient.Android.pas(98): H2443 Inline function 'TAndroidHelper.GetJActivity' has not been expanded because unit 'Androidapi.JNI.App' is not specified in USES list

 

Edited by Yaron
  • Like 1

Share this post


Link to post

Add the JAR file by going to Projects|Target Platforms|Android|Libraries.. Right click and select Add...

  • Like 1

Share this post


Link to post

Thanks Eli, Adding the library resolved the exception, however clicking on the choose file button still does nothing, so something else is preventing this from working in my own code and I can't seem to find out what.

 

I posted the App's source here:

https://inmatrix.com/temp/TestApp_src.zip

 

It just wraps around a website, simply run it and click 'connect' (no need for name and pass, it just opens a sample file submission page)

Share this post


Link to post

It seems that setting the WebChromeClient works only when the TWebBrowser is visible. Move your code that creates FWebManager to just after you set VoucherimWebBrowser.Visible to True.

  • Like 1

Share this post


Link to post
14 hours ago, Dave Nottage said:

It seems that setting the WebChromeClient works only when the TWebBrowser is visible. Move your code that creates FWebManager to just after you set VoucherimWebBrowser.Visible to True.

Sadly, this issue is not resolved.

Like you wrote, it only works if the TWebBrowser component is visible and I verified that it works using the very simple upload form you linked to originally.

However, when using it in a more complicated form, after browsing through several pages within the WebView to reach the form, clicking the button just terminates the App instantly (no UI error message).

Looking at the logcat, here's the output:

 

11-28 12:44:35.138  3138  3240 I InputDispatcher: Delivering touch to (3807): action: 0x0, toolType: 1
11-28 12:44:35.138  3807  3807 D ViewRootImpl: ViewPostImeInputStage processPointer 0
11-28 12:44:35.208  3138  3241 D InputReader: Input event(9): value=0 when=3853093882434000
11-28 12:44:35.208  3138  3241 D InputReader: Input event(9): value=0 when=3853093882434000
11-28 12:44:35.208  3138  3241 I InputReader: Touch event's action is 0x1 (deviceType=0) [pCnt=1, s=] when=3853093882434000
11-28 12:44:35.208  3138  3240 I InputDispatcher: Delivering touch to (3807): action: 0x1, toolType: 1
11-28 12:44:35.208  3807  3807 D ViewRootImpl: ViewPostImeInputStage processPointer 1
11-28 12:44:35.238  3807  3807 D Instrumentation: checkStartActivityResult() : Intent { act=android.intent.action.GET_CONTENT cat=[android.intent.category.OPENABLE] typ=.jpg }
11-28 12:44:35.238  3807  3807 D Instrumentation: checkStartActivityResult() : intent is instance of [Intent].
11-28 12:44:35.238  3138  4840 D ApplicationPolicy: isIntentDisabled start :Intent { act=android.intent.action.GET_CONTENT cat=[android.intent.category.OPENABLE] typ=.jpg }
11-28 12:44:35.238  3138  4840 D ApplicationPolicy: isIntentDisabled return :false
11-28 12:44:35.238  3807  3807 W System.err: android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.GET_CONTENT cat=[android.intent.category.OPENABLE] typ=.jpg }
11-28 12:44:35.238  3807  3807 W System.err: 	at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:1878)
11-28 12:44:35.238  3807  3807 W System.err: 	at android.app.Instrumentation.execStartActivity(Instrumentation.java:1545)
11-28 12:44:35.238  3807  3807 W System.err: 	at android.app.Activity.startActivityForResult(Activity.java:4283)
11-28 12:44:35.238  3807  3807 W System.err: 	at android.app.Activity.startActivityForResult(Activity.java:4230)
11-28 12:44:35.238  3807  3807 W System.err: 	at com.embarcadero.rtl.ProxyInterface.dispatchToNative(Native Method)
11-28 12:44:35.238  3807  3807 W System.err: 	at com.embarcadero.rtl.ProxyInterface.invoke(ProxyInterface.java:21)
11-28 12:44:35.238  3807  3807 W System.err: 	at java.lang.reflect.Proxy.invoke(Proxy.java:393)
11-28 12:44:35.238  3807  3807 W System.err: 	at $Proxy12.onFileChooserIntent(Unknown Source)
11-28 12:44:35.238  3807  3807 W System.err: 	at com.delphiworlds.kastri.DWWebChromeClient.onShowFileChooser(DWWebChromeClient.java:29)
11-28 12:44:35.238  3807  3807 W System.err: 	at N6.a(PG:145)
11-28 12:44:35.238  3807  3807 W System.err: 	at xp.runFileChooser(PG:2)
11-28 12:44:35.238  3807  3807 W System.err: 	at android.os.MessageQueue.nativePollOnce(Native Method)
11-28 12:44:35.238  3807  3807 W System.err: 	at android.os.MessageQueue.next(MessageQueue.java:323)
11-28 12:44:35.238  3807  3807 W System.err: 	at android.os.Looper.loop(Looper.java:143)
11-28 12:44:35.238  3807  3807 W System.err: 	at android.app.ActivityThread.main(ActivityThread.java:7225)
11-28 12:44:35.238  3807  3807 W System.err: 	at java.lang.reflect.Method.invoke(Native Method)
11-28 12:44:35.238  3807  3807 W System.err: 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
11-28 12:44:35.238  3807  3807 W System.err: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
11-28 12:44:35.248  1343  1516 D libEGL  : eglTerminate EGLDisplay = 0xb690164c
11-28 12:44:35.258  1343  4318 I SurfaceFlinger: id=17304 Removed TurfaceView (4/10)
11-28 12:44:35.258  1343  1522 I SurfaceFlinger: id=17304 Removed TurfaceView (-2/10)
11-28 12:44:35.268  1343  1343 D libEGL  : eglTerminate EGLDisplay = 0xbe93d3ac
11-28 12:44:35.888  3138  3240 W InputDispatcher: channel ~ Consumer closed input channel or an error occurred.  events=0x9
11-28 12:44:35.888  3138  3240 E InputDispatcher: channel ~ Channel is unrecoverably broken and will be disposed!
11-28 12:44:35.888  3138  4840 I WindowState: WIN DEATH: Window{891996c u0 d0 PopupWindow:8f2b053}
11-28 12:44:35.898  3138  3914 D GraphicsStats: Buffer count: 11
11-28 12:44:35.898  1343  1522 I SurfaceFlinger: id=17305 Removed QopupWindow (5/9)
11-28 12:44:35.898  1343 19954 D libEGL  : eglTerminate EGLDisplay = 0xb09a86fc
11-28 12:44:35.898  1343  1522 I SurfaceFlinger: id=17305 Removed QopupWindow (-2/9)
11-28 12:44:35.898  1343 19954 D libEGL  : eglTerminate EGLDisplay = 0xb09a86fc
11-28 12:44:35.898  3138  3920 I ActivityManager: Process com.inmatrix.Voucherim (pid 3807)(adj 0) has died(274,734)
11-28 12:44:35.898  3138  3920 D ActivityManager: cleanUpApplicationRecord -- 3807

 

Edited by Yaron

Share this post


Link to post

I also tried recreating the FManager component after each page load like this:
 

procedure TMainForm.WebBrowserDidFinishLoad(ASender: TObject);
begin
  FManager := nil;
    {$IFDEF TRACEDEBUG}AddDebugEntry('Create FWebManager (before)');{$ENDIF}
    Try
      FManager := TWebChromeClientManager.Create(WebBrowser);
    Except
      on E: Exception do
      begin
        {$IFDEF TRACEDEBUG}AddDebugEntry('Error creating FWebManager : '+E.Message);{$ENDIF}
      end;
    End;
    {$IFDEF TRACEDEBUG}AddDebugEntry('Create FWebManager (after)');{$ENDIF}
end;

But it didn't help, still crashes instantly after pressing the button.

Share this post


Link to post

I figured out why it's crashing in my code and not in the simple sample page,

If the HTML form file input has an "accept" field (e.g. "<input type="file" accept=".jpg, .jpeg" name="UploadImage" required>"), the App will crash.

 

Since I may not have control over the form's design, I am still looking for a solution around this issue.

 

You can easily test this with the code I linked in the previous post by replacing:

  WebBrowser.Navigate('https://ps.uci.edu/~franklin/doc/file_upload.html');

With:

  WebBrowser.LoadFromStrings(
  '<HTML><HEAD><TITLE>Test</TITLE></HEAD><BODY>'+
  '<form method="post" enctype="multipart/form-data" action="https://bla.com">'+
  '<input type="file" accept=".jpg, .jpeg" name="UploadImage" required>'+
  '<input type="submit" value="Save">'+
  '</form>'+
  '</BODY></HTML>','');

 

  • Like 1

Share this post


Link to post

It seems the problem is specific accept=".jpg, .jpeg"  or even  accept=".jpg" using accept="image/jpeg" works just fine.

 

Edited by Yaron
more data

Share this post


Link to post

I have followed the instructions you have given. But still an error appears:
[DCC Fatal Error] Unit1.pas (8): F2613 Unit 'DW.WebChromeClient.Android' not found.

I am using delphi 10.4.1

delphi error.jpg

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×