Flutter Webview - trapping touchstart events on Android - android

This issue posted on Github seems to suggest that touch events are deliberately suppressed in the Android implementation ostensibly to enable the detection of longpresses for the purposes of copy/paste.
My own application absolutely needs to be able to trap touch events - and longpress text selections have no relevance whatsoever. I thought I would do this by using a local copy of the Flutter webview implementation code, and then suppressing the longpress filter. The relevant code in webview_android.dart reads
We prevent text selection by intercepting the long press event.
This is a temporary stop gap due to issues with text selection on Android:
https://github.com/flutter/flutter/issues/24585 - the text selection
dialog is not responding to touch events.
https://github.com/flutter/flutter/issues/24584 - the text selection
handles are not showing.
TODO(amirh): remove this when the issues above are fixed.
onLongPress: () {},
To that end I downloaded the Flutter source and ensured that webview_flutter was available in a folder bearing that name two levels below my Flutter project root folder. After modifying pubspec.yaml to read
webview_flutter:
path: ../../webview_flutter
and running flutter clean && flutter run the APK was built and installed on my test device. However, it then reported
Setting up FlutterEngine.
D/FlutterActivityAndFragmentDelegate(24999): No preferred FlutterEngine was provided. Creating a new
FlutterEngine for this FlutterFragment.
W/FlutterEngine(24999): Tried to automatically register plugins with FlutterEngine
(io.flutter.embedding.engine.FlutterEngine#9480b1a) but could not find and invoke the
GeneratedPluginRegistrant.
I do not understand this message. I'd be most obliged to anyone who might be able to tell me how/whether it can be fixed.

I have now accidentally stumbled upon a way out of the "no touch events in Android" issue that dog Flutter Webview. Rather than delete my question I felt it would be more useful to others to leave it here with the solution I have found.
I started off with the intention of trapping Touch events in Flutter myself and then transmitting them to the WebView via evaluateJavascript. To that end I changed my Widget structure so as to wrap the WebView in a Listener. My entire Widget builder code is shown below.
#override Widget build(BuildContext context)
{
SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft]);
return WidgetsApp
(
onGenerateRoute:makeWebView,
color:Color.fromRGBO(0,128,255,1.0)
);
}
Route makeWebView(RouteSettings settings)
{
return new PageRouteBuilder
(
pageBuilder:(BuildContext context,Animation<double> animation,Animation<double>
secondaryAnimation)
{
return SafeArea
(
child:Listener
(
child: SizedBox.expand
(
child:WebView
(
debuggingEnabled:true,
initialUrl:'',
javascriptMode:JavascriptMode.unrestricted,
onWebViewCreated:registerController,
javascriptChannels:Set.from
([JavascriptChannel(name:'JSBridge',onMessageReceived:handleMessage)]),
),
),
)
);
});
}
Much to my surprise simply doing this made the encpasulated Flutter WebView respond to touch# events with no pesky LongPress issues getting in the way. It is not clear to me just why this should happen - perhaps someone else here can explain.
A few notes on my code above
I use WebViews that take up the entire device screen in landscape format - both on iOS and on Android
I use WidgetsApp rather than MaterialApp to provide the basic scaffolding
SafeArea ensures that the app consumes space that is legitimately available to it
Listener is what does the magic here and makes Touch events available in WebView, even on Android
WebView is the main player here where all the action happens. In front end JavaScript you just need to capture touchstart, touchmove and touchend events on the document.body.
You can read up more on the various widgets I have used here in the Flutter Docs.

Here is what I have found: as far as I can tell the official Flutter webview is a neglected step child. There are bugs dating back three years that have received little attention. Some of its "features" such as longPress suppression/detection are downright ridiculous.
I switched to using Flutter Webview plugin which fixes most of the issues I had encountered. Just remember to enable withzoom:true. I found that the webview does not automatically trap onPause and onResume events. I handled this as follows
#override void didChangeAppLifecycleState(AppLifecycleState state)
{
setState(()
{
appState = state;
switch(state)
{
case AppLifecycleState.inactive:
case AppLifecycleState.paused:
FlutterWebviewPlugin.evalJavascript('callBack()');break;//onPause
case AppLifecycleState.resumed:
FlutterWebviewPlugin.evalJavascript('callBack()');break;//onResume
}
});
}
You will have to ensure that
You have a suitably initialized instance of the FlutterWebviewPlugin singleton
In your Javascript you have defined callBack
I should mention that I start from WidgetsApp and the above code is in the stateful class it uses with with WidgetsBindingObserver

Related

Ionic 4: Hardware Back Button Reloading Application

Working on a Project and stuck in an Issue:
Hardware Back Button Reloading Application (I am using Angular Router in this application).
My Code to Exit Application:
ionViewDidEnter(){
this.subscription = this.platform.backButton.subscribe(()=>{
navigator['app'].exitApp();
});
}
ionViewWillLeave(){
this.subscription.unsubscribe();
}
While same logic Working in other applications. but in this application its reloading the application not exiting it.
P.S: i have also tried it to put in platform.ready() but no luck.
With IONIC 4, there is new method subscribeWithPriority developed to handle race between soft & hard back button. Try modifying your code like below:
this.platform.backButton.subscribeWithPriority(1, () => {
navigator['app'].exitApp();
});
subscribeWithPriority() stops the propagation of the event after its execution and if we subscribe with high priority and execute our prefered navigation instead of default one then it is going to work as you want.
More reference docs for details:
https://github.com/ionic-team/ionic/commit/6a5aec8b5d76280ced5e8bb8fd9ea6fe75fe6795
https://medium.com/#aleksandarmitrev/ionic-hardware-back-button-nightmare-9f4af35cbfb0
UPDATES:
Try using this new version of exitApp cordova plugin. I haven't
tried myself but looks promising from popularity.
Also try to empty the page stack from Navcontroller or go to your home screen, seems like that's causing the reload for app with sidemenu's & tab pages... this.navCtrl.pop() / this._navCtrl.navigateBack('HomeScreen'), and then call exitApp.
NOTE: Tabs & SideMenu as those have its own routing module does create lot of complexity with app navigation.
Solved:
As Mention by #rtpHarry template of SideMenu / Tabs have History which leads application to Reload it self on root page. i was able to solve this by clearing History.
ionViewDidEnter(){
navigator['app'].clearHistory();
}
on Your Root Page just Clear your history and your Hardware Back Button will close the Application instead of Reloading it.
Do you have a sidemenu in your app? I'm just curious because this seems to be when I get this problem as well.
If you look in your inspector, you will see that window.history has a length of 1.
I don't see it in some of my apps, but the app that I have a side menu setup acts this way - on the homepage if you press back the screen goes white then it reloads the app.
Like I say, looking in the inspector shows that there is a history to step back to, which it is trying to do, and whatever that history step is, it just pushes it forward back to the homepage, which made me wonder if it was the sidemenu setting up its own control of the navigation system.
I've probably said some poorly worded terminology but as I haven't solved this myself I thought I would just let you know what I had found... hopefully it helps you move forward.
In my scenario, I wasn't even trying to do the exit on back code - I just noticed that the app would appear to "reboot" if I kept pressing back.
This explain the solution on Ionic 5 (and 4.6+ too I think).
private backButtonSub: Subscription;
ionViewDidEnter() {
this.backButtonSub = this.platform.backButton.subscribeWithPriority(
10000,
() => {
// do your stuff
}
);
}
ionViewWillLeave() {
this.backButtonSub.unsubscribe();
}
also keep
IonicModule.forRoot({
hardwareBackButton: true
}),
to true (default) in your app.module.ts
Sources:
https://www.damirscorner.com/blog/posts/20191122-CustomizingAndroidBackButtonInIonic4.html
The Doc

Android flutter's flutter_webview_plugin plugin's webview shows loading indicator and nothing else

I have been building a flutter android app with flutter_webview_plugin. My problem is when I use back button of android phone the web view rather than closing shows the loading indicator and nothing else.
To solve this problem we will have to listen both the webview's onDestroy status and navigator's pop-ability and then we will pop the webview route (screen).
To do this we will just write this code within the build method where the WebviewScaffold is returned.
flutterWebviewPlugin.onDestroy.listen((_){
if(Navigator.canPop(context)){
Navigator.of(context).pop();
}
});
Problem solved :)
note flutterWebviewPlugin is an object of FlutterWebviewPlugin declared out of the build method.

Touchmove & vmousover not working in a Cordova/PhoneGap Android app

I've tried both techniques in this answer to get a "dragging touch highlight" across elements in my PhoneGap App (testing on Android).
Here's my JSFiddle of the touchmove approach
$("td").bind("touchmove", function(evt){
var touch = evt.originalEvent.touches[0]
highlightHoveredObject(touch.clientX, touch.clientY);
});
Here's my JSFiddle of the vmousemove approach
$("#main").bind("vmousemove", function(evt){
$('.catch').each(function(index) {
if ( div_overlap($(this), evt.pageX, evt.pageY) ) {
$('.catch').not('eq('+index+')').removeClass('green');
if (!$(this).hasClass('green')) {
$(this).addClass('green');
}
}
});
});
Both work perfectly when emulating the app from desktop browser. Both work when viewing the JSFiddles from my Android tablet browser. But in the installed app on the tablet, it doesn't work. Instead of an updating highlight as I drag across the elements, all I get is a highlight on the first-touched event. The same for both methods.
Any ideas what's going on?
A comment on this question has an intriguing suggestion that "If you are running on android you also need to cancel the touchmove event to get new ones while touching. Don't ask me why...". Does that ring a bell, and if so, how would I "cancel the touchmove event to get new ones" with either of these approaches?
Alternately, has anyone successfully done a "dragging highlight" effect on a PhoneGap app, and would you care to share your technique?

kendo UI mobile ListView hang after opening external link on native browser

In a Kendo UI Mobile ListView, a script to open an external link by native browser is called when a link is clicked.
The PhoneGap script is as follow:
On Android:
navigator.app.loadUrl(link, { openExternal:true } );
On iOS:
window.open(link, '_system');
The link can be opened on the corresponding native browser.
However, when the user switch back to the app from the native browser, some problems happen.
On Android, the screen hung on the original view, when the back button is pressed again, the screen is un-freezed and can be refreshed.
On iOS, however, the screen is also hung on the original view. When tapped on the screen, the complete view (with the layout) is moved. There is no way to un-freeze this screen.
How to fix this so that the screen can be un-frezzed after switching back from the native browser to the app?
Thank you very much for your help.
Updated 1:
I changed the original tag to a tag, everythings work now. But I am still curious to see if it is certain kind of bugs for Kendo UI Mobile.
There is a serious problem with Kendo Mobile hanging the page completely, making the app totally unresponsive to touch/mouse. The offending CSS is in Loader.transition() which does this.container.css("pointer-events", "none") which is equivalent to:
document.body.style.pointerEvents = "none";
Ouch - that is ugly. Plus in _attachCapture there is offensive JavaScript for all mouse and touch events that does:
event.preventDefault();
Fatal if using an app with an embedded full page WebView/UIWebView (requiring app to be closed and restarted).
Hangs can happen if:
You have an exception in your code (even in unobvious places),
You mistype a transition (no exception, just hangs),
A user's browser doesn't fire the transitionEnd event properly for some reason (This was repeatable for one user's up-to-date Chrome browser.
There is a failure mode in the Interaction between page transitions and Loader (depending on timing, couldn't repeat),
Multiple other causes
Note that there is a comment in Kendo that says: "This should be cleaned up at some point (widget by widget), and refactored to widgets not relying on the complete callback if no transition occurs.", so clearly Telerik know there is a problem.
You can use the following code during development to at least warn when Kendo Mobile has crapped itself:
var transitionTimer;
kendo.mobile.ui.Loader.prototype.wasTransition = kendo.mobile.ui.Loader.prototype.transition;
kendo.mobile.ui.Loader.prototype.transition = function() {
transitionTimer = setTimeout(function() {
alert('Kendo has hung the page');
}, 10000);
this.wasTransition.apply(this, arguments);
}
kendo.mobile.ui.Loader.prototype.wasTransitionDone = kendo.mobile.ui.Loader.prototype.transitionDone;
kendo.mobile.ui.Loader.prototype.transitionDone = function() {
clearTimeout(transitionTimer);
this.wasTransitionDone.apply(this, arguments);
}

What DOM events are available to WebKit on Android?

I'm building a mobile web app targeting Android users. I need to know what DOM events are available to me. I have been able to make the following work, but not terribly reliably:
click
mouseover
mousedown
mouseup
change
I have not been able to get the following to work:
keypress
keydown
keyup
Does anyone know the full list of what is supported and in what contexts (e.g., is onchange only available to form inputs?)? I can't find a reference for this on The Googles.
Thanks!
Update: I asked the same question on the Android developers list. I will be doing some more testing and will post my results both here and there.
OK, this is interesting. My use case is that I have a series of links (A tags) on a screen in a WebKit view. To test what events area available, using jQuery 1.3.1, I attached every event listed on this page (even ones that don't make sense) to the links then used the up, down, and enter controls on the Android emulator and noted which events fired in which circumstances.
Here is the code I used to attach the events, with results to follow. Note, I'm using "live" event binding because for my application, the A tags are inserted dynamically.
$.each([
'blur',
'change',
'click',
'contextmenu',
'copy',
'cut',
'dblclick',
'error',
'focus',
'keydown',
'keypress',
'keyup',
'mousedown',
'mousemove',
'mouseout',
'mouseover',
'mouseup',
'mousewheel',
'paste',
'reset',
'resize',
'scroll',
'select',
'submit',
// W3C events
'DOMActivate',
'DOMAttrModified',
'DOMCharacterDataModified',
'DOMFocusIn',
'DOMFocusOut',
'DOMMouseScroll',
'DOMNodeInserted',
'DOMNodeRemoved',
'DOMSubtreeModified',
'textInput',
// Microsoft events
'activate',
'beforecopy',
'beforecut',
'beforepaste',
'deactivate',
'focusin',
'focusout',
'hashchange',
'mouseenter',
'mouseleave'
], function () {
$('a').live(this, function (evt) {
alert(evt.type);
});
});
Here's how it shook out:
On first page load with nothing highlighted (no ugly orange selection box around any item), using down button to select the first item, the following events fired (in order): mouseover, mouseenter, mousemove, DOMFocusIn
With an item selected, moving to the next item using the down button, the following events fired (in order): mouseout, mouseover, mousemove, DOMFocusOut, DOMFocusIn
With an item selected, clicking the "enter" button, the following events fired (in order): mousemove, mousedown, DOMFocusOut, mouseup, click, DOMActivate
This strikes me as a bunch of random garbage. And, who's that cheeky IE-only event (mouseenter) making a cameo, then taking the rest of the day off? Oh well, at least now I know what events to watch for.
It would be great if others want to take my test code and do a more thorough run through, perhaps using form elements, images, etc.
Since this is the second most popular Android + JavaScript post on SO (which is just a sad commentary on the state of web development targeting the Android platform), I thought it may be worthwhile including a link to pkk's touch event test results at http://www.quirksmode.org/mobile/tableTouch.html and also http://www.quirksmode.org/mobile/ in general.
As of Android 1.5, the same touch(start|move|end|cancel) events that the iPhone supports work in Android as well.
One problem I found was that touchmove ends get queued up. No workaround yet.

Categories

Resources