Jump to content

[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]
Photo

native.showPopup() does not return to my app when...
Started by ksan Apr 22 2014 02:02 PM

28 replies to this topic
[TOPIC CONTROLS]
Page 1 of 2 1 2
[/TOPIC CONTROLS]
[modOptionsDropdown]
[/modOptionsDropdown]
[reputationFilter]
[TOPIC: post.html]
#1

ksan

[GLOBAL: userInfoPane.html]
ksan
  • Pro
  • PipPipPipPipPipPip
  • 2,732 posts
  • Jedi

I use native.showPopup("mail", options) in a number of regular, storyboard scenes and it works great. I send my email or cancel and in either case my app comes back to view with the storyboard scene that took us to mail app where I left it. All is good. 

 

I am now implementing a webView in my app to show some local HTML. Inside the webView's listener I catch it when user taps on a mailto: link, such as a Contact Us kind of link with an embedded mailto:xyz@abc.com type email address.

 

In the webView listener I call native.showPopup("mail", options) when user clicks that Contact Us link so I can send the email and return back to my webView but unfortunately native.showPopup() behaves differently in this case. When called from a webView it does not return to my app and when the mail is sent it returns to the inbox of the mail app. 

 

Has anyone else noticed this anomaly and figured out a way around it? Thank you very much for your help.



[TOPIC: post.html]
#2

ksan

[GLOBAL: userInfoPane.html]
ksan
  • Pro
  • PipPipPipPipPipPip
  • 2,732 posts
  • Jedi

Turns out for some weird reason, webView:stop() does not work when the link clicked is a mailto: link. It works for all other links that would have opened the link inside the webView itself but mailto: that sends you to the native app at OS level can't be stopped with webView:stop(). Since apparently I wasn't able to stop the webView sending me to the native mail app I wasn't able to even get the native.showPopup("mail", options) to show. Managed to solve it using a little hack... Here goes... 

 

Since I have control over the html help file I simply changed the mailto link to a regular link while keeping the abc@xyz.com as the visible link text. I simply placed "test" as the link URL into my HTML. In my webView listener I listen and check the URLs as they get clicked. I look for "test" inside the URL clicked. If I find it I call webView:stop() and then call native.showPopup("mail", options) instead. 

 

This is now working and I am able to get back to my webView scene after the mail is sent. Hope this helps others who might be stuck with the same thing in time to come. 

 

Regards,

Kerem



[TOPIC: post.html]
#3

Gremlin Interactive

[GLOBAL: userInfoPane.html]
Gremlin Interactive
  • Enterprise
  • PipPipPipPipPipPip
  • 700 posts
  • Jedi

Nice workaround :) I was going to suggest something similar but I saw you already solved it.

[TOPIC: post.html]
#4

ksan

[GLOBAL: userInfoPane.html]
ksan
  • Pro
  • PipPipPipPipPipPip
  • 2,732 posts
  • Jedi

Thanks. I hope the API can be fixed so that webView:stop() will actually stop the loading of a mailto: link as well. This is the only way to get a consistent UX in the cases where app user goes to third-party web pages where we don't control the way links are built. 



[TOPIC: post.html]
#5

Joshua Quick

[GLOBAL: userInfoPane.html]
Joshua Quick
  • Corona Staff
  • 2,615 posts
  • Jedi

The webView:stop() function can only stop the loading of web pages.  Tapping a mailto:// link causes the WebView to launch your device's default mail app (not a popup), which is not a web page.  That's why the webView:stop() function won't stop it.  And since the mailto:// is launching your Mail app, that's why your trapped in that app.

 

If you want the end-user to remain in your app, then the solution is to set up a Lua listener to trap the URL request for the mailto:// URL scheme and return true to override iOS' default behavior.  That's your opportunity to display a mail popup within your own app via the native.showPopup().  This sounds like what you're doing now, which is the right solution.

 

Just note that Android's WebView behavior is completely different.  Android's native WebViews will not display the mail app by default when tapping a mailto:// URL scheme.  Instead, the WebView will attempt to load the mailto:// as a webpage, which of course will fail and display an error page in the WebView instead.  So, on Android, you have no choice but to override the URL request and implement it yourself.  This is actually how it works at the native level on Android.  Unfortunately, even if you do override the URL request, you'll still see a failed to load webpage flashed onscreen.  There really isn't a nice way to handle this on Android, which makes what you're doing overly difficult.



[TOPIC: post.html]
#6

ksan

[GLOBAL: userInfoPane.html]
ksan
  • Pro
  • PipPipPipPipPipPip
  • 2,732 posts
  • Jedi

Hi Joshua, 

 

Thanks for your input and help. I will try "return true" in my URL trap for "mailto". I didn't think of return true to stop the webView from loading the external mail app so I created this "broken link" hack which meant the webView could not load anything and I was able to trap this and trigger the native.showPopup etc. Your idea should make for a more refined solution and would probably get rid of that one blink I'm getting when user taps on the broken link.

 

So it sounds like this solution is the only way to deal with this issue on Android as well. Will test later today and report back.

 

Once again thank you very much.

 

Regards,

Kerem



[TOPIC: post.html]
#7

ksan

[GLOBAL: userInfoPane.html]
ksan
  • Pro
  • PipPipPipPipPipPip
  • 2,732 posts
  • Jedi

Hmmm. You were absolutely right. My hack works well on IOS but not on Android. webView on IOS discards the bad URL, blinks and moves on. On Android it brings up a message telling me that my URL is broken. 

 

So I will improve my listener with the return true in addition to the "broken URL" hack. Fingers crossed.

 

Thanks once again for your help.



[TOPIC: post.html]
#8

ksan

[GLOBAL: userInfoPane.html]
ksan
  • Pro
  • PipPipPipPipPipPip
  • 2,732 posts
  • Jedi

Ok. On Android return true or webView:stop() still does not limit the loading of the error page. Funny thing is how my url is no longer a mailto but still gets through webView:stop(). Problem is that once I return from the mail app the error page is there. I will try to detect when I'm on Android and reload the original help HTML file in the meanwhile. If you have any ideas to make this look a little better or get more straightforward I'm all ears. Thanks much!!!



[TOPIC: post.html]
#9

ksan

[GLOBAL: userInfoPane.html]
ksan
  • Pro
  • PipPipPipPipPipPip
  • 2,732 posts
  • Jedi

Joshua, I re-read your post above and you clearly mention return true is the way to override that URL request on IOS. Is there a similar way to override the request on Android? I'm ok with the brief flash of that message as long as I can throw it away and return to the original html. Thanks



[TOPIC: post.html]
#10

ksan

[GLOBAL: userInfoPane.html]
ksan
  • Pro
  • PipPipPipPipPipPip
  • 2,732 posts
  • Jedi

Found a way. One hack brings on another one... I found that if I throw a webView:back() into the URL trap and make it conditional on Android I see the flashing error page but I end up back in my caller html. All good. Thanks for your help.

 

Edit : Throws hands in the air while running around the room... No joy... 



[TOPIC: post.html]
#11

Joshua Quick

[GLOBAL: userInfoPane.html]
Joshua Quick
  • Corona Staff
  • 2,615 posts
  • Jedi

I'm sorry.  I had it backwards.  You need to return "false" from your urlRequest to override the request.  Returning true (or not returning a value at all) tell its to go ahead and load the URL.

   http://docs.coronalabs.com/api/library/native/showWebPopup.html#example

 

Our sample app "Interface/WebOverlay" that is included with the Corona Simulator shows an example of this.  There is a "corona:close" URL in the HTML which when tapped on will be overridden by the "main.lua" to close the web popup.



[TOPIC: post.html]
#12

ksan

[GLOBAL: userInfoPane.html]
ksan
  • Pro
  • PipPipPipPipPipPip
  • 2,732 posts
  • Jedi

Ahh there you go. I know you were testing me and I failed!  :)

 

Thanks much for the follow-up. This will be ok I'm sure. 



[TOPIC: post.html]
#13

ksan

[GLOBAL: userInfoPane.html]
ksan
  • Pro
  • PipPipPipPipPipPip
  • 2,732 posts
  • Jedi

Joshua, 

 

This is a little bizarre. I used the return false method you pointed me towards in the CL sample as well as the API page but on Android it seems that return false has absolutely no control over webView. Please note that the API doc and the sample code both use the older native.showWebPopup and I'm trying to use the native.newWebView(). Not sure if this might be a problem but it seems like native.newWebView() is totally ignoring that return false and loading the linked page.

 

By the way, just for experimentation, I changed my link from a mailto link to a regular URL link to another local html file. Its an almost empty file with a simple Back link in it pointing to my original html file. 

 

Anyways, the listener is below in case you can spot a mistake. Thanks for your help.

 

    local function webListener(event)

        local url = event.url
        print("showWebPopup callback ", url)
        
	local shouldLoad = true

	if string.find( url, "back.html" ) ~= nil then
		-- Close the web popup
		shouldLoad = false
        else
            if webView.canGoBack then 
                btnBack.alpha = 1
            else
                 btnBack.alpha = 0
            end
	end
        
        print(shouldLoad)
	return shouldLoad                
    end


[TOPIC: post.html]
#14

ksan

[GLOBAL: userInfoPane.html]
ksan
  • Pro
  • PipPipPipPipPipPip
  • 2,732 posts
  • Jedi

Printed a lot from the webView listener to the console and observed something interesting. After I return false in one cycle, the webView listener stops responding on Android. Seems like the return false is causing a crash of sorts with no errors. After this point none of the links work because the listener stops responding. 

 

I also noted the native.webView API page does not have the return true in the example. http://docs.coronalabs.com/api/library/native/newWebView.html

 

wondering if we're not supposed to use return true / false with webView. 

 

As a follow-up, since I'm now using a regular URL and trapping this in my listener, I thought webView:stop should work. It does work %90 of the time (rough estimate) and sometimes it slips. I'm thinking the time it takes for my string.find() to scan the URL and trigger webView:stop() is sometimes too much and the page loading begins just like that since the webView is not waiting for the listener to return true... 

 

Something is definitely fishy here. Thoughts?



[TOPIC: post.html]
#15

ksan

[GLOBAL: userInfoPane.html]
ksan
  • Pro
  • PipPipPipPipPipPip
  • 2,732 posts
  • Jedi

Hmm, seems like the issue was not return true/false but webView:stop()

 

Once I trap the back.html and keep it from loading using webView:stop() none of the other links work and I don't get the listener fire anymore. Not sure why or what I might be doing wrong. 



[TOPIC: post.html]
#16

ksan

[GLOBAL: userInfoPane.html]
ksan
  • Pro
  • PipPipPipPipPipPip
  • 2,732 posts
  • Jedi

Ok. Built the same code on IOS, webView:stop() nicely stops back.html from loading and next time I tap another link that one loads well. No problems. Same behavior on Mac Simulator. On Android however, webView listener gets hosed the second I use webView:stop(). Seems like a bug to me. What do you think? Thanks for all your help.



[TOPIC: post.html]
#17

Joshua Quick

[GLOBAL: userInfoPane.html]
Joshua Quick
  • Corona Staff
  • 2,615 posts
  • Jedi

Okay... I just had someone here school me on how this really works.

 

If you are using a WebPopup, then you override the URL request by returning false.

 

If you are using a WebView, then you can only override the request by calling the stop() function.  The Lua listener does not accept a return value.  The reason the WebView's Lua listener has a different behavior is because it is also used to provide other events such as a "loaded" event to let you know when the page has finished loading.

 

Regarding calling stop() on Android still displaying an error page, this is exactly the behavior that I expected and what I was warning you about up above.  What you're trying to do will only work on iOS.  Unfortunately, I don't think there is a good solution on Android.  The problem with Google's WebView is that we can't reliably catch the URL request until the request has already been sent.  This means that is already too late to stop it.  The best we can do is block the response when you return false for a WebPopup or call stop() on a WebView.  Now, this works fine when the URL is to a real web page, but for an invalid URL to a web page, you'll always get an error page displayed.  This is an unfortunate quirk with Google's WebView.  While it's fair to call it a bug, it's unfortunately a bug that we're stuck with and I don't a reliable resolution to.

 

Now, let me ask you this.  Are you using the WebView to display a local HTML file as a means of display a nice GUI to the end-user?

That is, you're WebView is not intended to "navigate" to other web pages on the Internet?  If so, then it sounds like what you need is a new API that allows you to display an HtmlView which does not support any navigation at all.  That is something we can definitely implement reliably on all platforms because we can block all navigation, but still provide feedback of what was tapped on.  I'm just trying to think outside of the box here of what might be a good solution to this problem.  If this is what you're after, then I can write it up as a feature request.



[TOPIC: post.html]
#18

ksan

[GLOBAL: userInfoPane.html]
ksan
  • Pro
  • PipPipPipPipPipPip
  • 2,732 posts
  • Jedi

Hi Joshua, 

 

Thank you so much for taking the time to learn more about this issue and report back to share your findings. This is matching exactly what I found. I replaced the error inducing badly formed URL call at some point with a call to another local html and I found that webView:stop could not catch it. Then I tried stopping real web sites from loading and I could stop them. This matches exactly with your comments. I understand the issue with Google bug so will try to live within our means.

 

My use case is an information screen I created for my app. I am using local html & webView combination because I am using formatting, image inserts (logos etc) and links to external reference material. So as nice as your idea sounds I am afraid I need to keep with the webView as it is and find a way to make it work. Thank you very much for thinking outside the box though. 

 

I think I'll simply hide a www.google.com call in the link that I want to trap and use webView:stop(). I think that might work reasonably well. Will report back. 

 

Regards & thanks.

 

EDIT : Confirmed. This approach is working very well on Android. My link in html looks like this : Please contact us at info@appynerds.com and behind the scenes the link setup is not a mailto but a real URL pointing to a contact us page on my website. I decided to use this instead of Google in case at some point this link still slips through webView:stop() and ends up showing. This way it will still be showing something relevant. Anyways, since its a slow loading normal website I can trap it and stop it loading using webView:stop() and redirect my app flow to native.showPopup("mail", options). Once I'm done sending the mail app flow returns back to my local html. All good. 

 

EDIT2: I realize the email app chosen on Android can have a say in the outcome as well... When I hit the email link which triggers native.showPopup("mail", options) my Android device tells me which email app I would like to use. Email (built-in app) and Gmail are the options available to me but I imagine other devices may come with other options (ie Samsung Mail app etc). When I use built in email and send my message I am returned nicely to my webView. When I use Gmail it still returns to my app but to the initial view, as if my app is restarted...

 

EDIT3: On Android 2.3.x after I return from built in email app the keyboard doesn't disappear. I'll have to put some code in there to get rid of it... I think I really dislike Android... 



[TOPIC: post.html]
#19

Joshua Quick

[GLOBAL: userInfoPane.html]
Joshua Quick
  • Corona Staff
  • 2,615 posts
  • Jedi

>> Confirmed. This approach is working very well on Android.

 

Just curious... does this work well when your Android device has no Internet connection?

 

>> When I use Gmail it still returns to my app but to the initial view, as if my app is restarted...

 

It sounds like the Android OS terminated your app while you were in the Gmail app.  There should be some kind of indication of this in the Android log, which you can view via "adb logcat" or "ddms".  This typically happens if the app in the foreground (in this case, gmail) needs more memory than what is available on the device, forcing the OS to terminate other apps in the background.  By default, Corona releases as much memory as possible when being suspended to avoid this issue, such as releasing all images/textures from memory.  This is especially important for the older cheaper devices which have little RAM available.  I've never seen this happen with Gmail on my devices, but perhaps your email has a lot more files in it than mine which is pushing it to require more memory?  I'm not sure.  Any case, you can confirm that this is the case via the Android log.  If it is the case, then unfortunately I don't think there is anything we can do about it.

 

>> On Android 2.3.x after I return from built in email app the keyboard doesn't disappear.

 

That's pretty typical on Android.  It's actually not a big deal because, unlike iOS, end-user have the ability to clear the onscreen keyboard themselves on Android by pressing the Back button or a dismiss button on the virtual keyboard (typically only shown on tablet keyboards).



[TOPIC: post.html]
#20

ksan

[GLOBAL: userInfoPane.html]
ksan
  • Pro
  • PipPipPipPipPipPip
  • 2,732 posts
  • Jedi

Hi Joshua, 

 

Thanks for your follow-up. Quick responses below : 

 

>> Just curious... does this work well when your Android device has no Internet connection?

 

Just tested this scenario. I get the mail app and my message is put in the outbox and then I'm returned to my app where I end up in the Android's "page could not be found display" for the real internet page I tried to go to and trap in my listener. Thanks for the hint. I will try and see if I can redirect to my initial local html in these cases. 

 

>> When I use Gmail it still returns to my app but to the initial view, as if my app is restarted...

 

>> It sounds like the Android OS terminated your app while you were in the Gmail app.  ... This is especially important for the older cheaper devices which have little RAM available.  ... in any case, you can confirm that this is the case via the Android log.  If it is the case, then unfortunately I don't think there is anything we can do about it.

 

Hit the nail right on the head again. I observed this issue on my old Samsung Galaxy S running Android 2.3.5. It is my low end tester device where I look at the worst case scenario. I think this is understandable and I can explain it if anyone complains. Nothing to do.

 

>> That's pretty typical on Android.  It's actually not a big deal because,...

 

Yup. No biggie. I just placed a " native.setKeyboardFocus( nil )" in my onApplicationResume listener and we're all good. Keyboard nicely gets tucked away when I return to my app.



[TOPIC: post.html]
#21

Joshua Quick

[GLOBAL: userInfoPane.html]
Joshua Quick
  • Corona Staff
  • 2,615 posts
  • Jedi

I just took a quick look at our WebView code on Android.  Your Lua listener will receive an error code if it fails to load the URL.

   http://docs.coronalabs.com/api/event/urlRequest/errorCode.html

 

So, what you should do is check if "event.errorCode" is *not* nil as shown in the link above.  If you did receive an error code, then you can either reload your webpage with your local HTML file... or call the webView:back() function.

 

I hope this helps.



[TOPIC: post.html]
#22

ksan

[GLOBAL: userInfoPane.html]
ksan
  • Pro
  • PipPipPipPipPipPip
  • 2,732 posts
  • Jedi

Hi Joshua, 

 

Thank you very much for looking into what my options could be. I tried your suggestion and find that I can detect the error but still end in that page. Perhaps its once again the speed at which this thing is going through. My listener triggers are a little interesting though. See below output from my logcat and my commentary (marked as ^^ ). Sorry for the length of this.

 

I/Corona  (29621): Event type & URL is     other file:///data/user/11/com.appynerds.xxx/app_data/infoFinal.html
 
^^ above, first time the listener is called for initial load request. Event type is other.
 
I/Corona  (29621): Event type & URL is     loaded file:///data/user/11/com.appynerds.xxx/app_data/infoFinal.html
 
^^ above, second time the listener is called for initial loading of my local html. Event type is loaded.
 
I/Corona  (29621): Event type & URL is     link http://appynerds.com/index.php/contact
 
^^ above, I tap on the fake link pointing to my contact page. This is what I want to trap. Now the internet is off so this is supposed to return an error. Event type is link.
 
I/Corona  (29621): Event type & URL is      http://appynerds.com/index.php/contact
 
^^ above, fourth time the listener is called. event type is blank. URL is the last link tapped now coming back with an error. So far so good.
 
I/Corona  (29621): error code     -2
 
^^ further down in my listener I check for error and print the code. Still the same listener call (ie fourth time it is running)
 
I/Corona  (29621): Event type & URL is     history file:///data/user/11/com.appynerds.xxx/app_data/infoFinal.html
 
^^ in my error trap I call webView:back. What you see above is the 5th time the listener is called. How the event type is history and url is my original local html. 
 
I/Corona  (29621): Event type & URL is     loaded http://appynerds.com/index.php/contact
 
^^ Now this is bizarre... All was good until the 5th listener trigger and I was expecting to land in my original html page with an event type of loaded. I end up with a loaded for my contact page which gave me the error in the first place. Since internet is down I also end up with the Android's page not loaded message. Yikes. 
 
So this 6th triggering of the web listener going back to the error page instead of obeying the webView:back is something I need to understand a little more and avoid if possible. 
 
Any thoughts? 


[TOPIC: post.html]
#23

Joshua Quick

[GLOBAL: userInfoPane.html]
Joshua Quick
  • Corona Staff
  • 2,615 posts
  • Jedi

Yeah, that's why I was thinking it might be better for you to reload your local HTML when an error is detected.  That may be more reliable than going back a page.  I'm think of the case where the WebView quickly loads more than one error page back-to-back.  That's what I think is happening.



[TOPIC: post.html]
#24

ksan

[GLOBAL: userInfoPane.html]
ksan
  • Pro
  • PipPipPipPipPipPip
  • 2,732 posts
  • Jedi

Yup. This is working better. I can see the error page briefly flash in between but I do end up where I need to. Given the offline is not a common use case for someone wanting to hit my email link I think its a safe bet that I can live with this solution. Thank you so much for your help.

 

Last question. Is there a way to reset back/forth history in the webView? Reason I'm asking is that, in this case we've been discussing, once user hits the email link and comes back to the initial page I still get my "Back" button visible since webView.canGoBack is true at this point. What I need to do is to somehow get webView know that I'm reloading my initial html so therefore there is no Back to go to. In this case if you hit back you end up going to the sendmail link. 

 

I think I can look at the URL and not make the back button visible if I'm on the initial html so I can deal with this programmatically but just asking if I could get webView.canGoBack reset. 

 

Thanks!!!



[TOPIC: post.html]
#25

Joshua Quick

[GLOBAL: userInfoPane.html]
Joshua Quick
  • Corona Staff
  • 2,615 posts
  • Jedi

>> Last question. Is there a way to reset back/forth history in the webView?

 

Unfortunately no.  The only solution I can think of is to destroy and re-create the WebView, which is a bit heavy handed.

 

I also don't recommend that you call the webView:back() function multiple times in a loop either because it performs that operation asynchronously, meaning that the canGoBack property won't update until after the back operation finishes loading the previous page.

 

That said, I've seen a Corona developer destroy and re-create a WebView to reset the navigation history before.  What he did was create his own loading object made up of a white rectangle a text object that was positioned behind the native WebView.  This way it doesn't look like the WebView quickly disappears and re-appears to the end-user, which would look ugly.  I also think he hid the WebView when loading a webpage on the Internet and then showed it once it finished loading via its Lua listener... which came in handy for websites that take a long time to load.  This was especially useful since the native WebView doesn't actually show any load progress so that the end-user won't think something is wrong.




[topic_controls]
Page 1 of 2 1 2
 
[/topic_controls]