I am working on the migration of a JavaScript app from Sencha Touch 1.x to Sencha Touch 2.x. It is designated to run in a WebView inside a native Android app. The app worked on Sencha Touch 1.x. Meanwhile there has been a lot of refactoring and debugging to make it run on Sencha Touch 2.x. The current Sencha Touch version being used is 2.4.2.
I did some testing in a desktop version of Firefox. It basically worked out, but testing is limited since the Sencha Touch app is highly dependent on several objects and methods provided by the native Android app via JS-interface.
Now, when it comes to running it inside the Android WebView I am facing a strange issue: After startup and rendering all I get to see is nothing but a white screen, whereas all MVC-dependencies are loaded and the launch() in Ext.application is being triggered properly. There are no JavaScript errors in the console at all and all Ext classes/objects/methods are available.
This is the basic setup of my Ext.application block:
Ext.application({
name: 'MyApp',
models: [
'TestModel',
'UserModel',
'HauptmenueModel',
/* ... */
],
controllers: [
'MyController'
],
views: [
'Benutzer',
'Startseite',
/* ... */
],
stores: [
'TestStore',
'UserStore',
'HauptmenueStore',
/* ... */
],
launch: function () {
Ext.Viewport.add(Ext.create('MyApp.view.Startseite'));
console.log(document.body.innerHTML.length);
}
});
The views (e. g. the MyApp.view.Startseite view) are defined quite conventional:
Ext.define('MyApp.view.Startseite', {
extend: 'Ext.Panel',
config: {
id: 'Startseite',
layout: 'card',
items: [
/* ... */
]
},
/* ... */
});
What is interesting: Sencha Touch does indeed generate a lot of HTML content depending on the view I am loading via Ext.Viewport.add(), which I can deduce from the innerHTML.length I am logging. But nothing shows up in the WebView. The size consequently changes if I create a different view and load it via Ext.Viewport.add() but the viewport remains white.
What could be the reason for this? I am also very thankful for any tips on debugging this or somehow isolating this issue.
A possible reason for this could be the WebView’s layout itself. Please check the layout height of the WebView. Setting it to MATCH_PARENT should do the job.
It is recommended to set the WebView layout height to a fixed value or
to MATCH_PARENT instead of using WRAP_CONTENT.
See the Layout size section here:
http://developer.android.com/reference/android/webkit/WebView.html
Related
Context: I am developing a mobile Shiny app using the shinyMobile package, which is a wrapper for the famous framework7 HTML template.
In my app, the user has to make a selection of attributes on a first tab using multiple dropdown lists, and then, on the other tabs, some output is produced. Each tab requires the user to scroll up and down to access all the content and in this process, very often the 'pull to refresh' feature is triggered.
This is really annoying, because the entire attribute selection and output are lost, and the user has to start over from scratch.
What I tried: based on this SO thread which pointed me to this Google developer page, I tried setting the CSS overscroll-behavior property to contain with: body {overscroll-behavior-y: contain;}. PROBLEM: It does not work for me! (tested on Chrome Android)
Minimal reproducible example:
default app, deployed here
library(shiny);library(shinyMobile)
shiny::shinyApp(
ui = f7Page(
f7Card(
h5('try to pull to refresh. normally it should work.')
)
),
server = function(input, output) {}
)
Supposedly fixed app, deployed here
library(shiny);library(shinyMobile)
shiny::shinyApp(
ui = f7Page(
tags$style(type='text/css', '.body{overscroll-behavior-y: contain;}'),
f7Card(
h5('try to pull to refresh. Normally it should not work.')
)
),
server = function(input, output) {}
)
Hope you guys can reproduce my issue and identify what is amiss!!!
You might want to change your css part to: html,body{overscroll-behavior-y: contain;}, see https://stackoverflow.com/a/42509310/3502164.
Then it works for me on my mobile (android chrome).
Reproducible example:
library(shiny)
library(shinyMobile)
app <- shiny::shinyApp(
ui = f7Page(
tags$style(type='text/css', 'html,body{overscroll-behavior-y: contain;}'),
f7Card(
h5('try to pull to refresh. Normally it should not work.')
)
),
server = function(input, output) {}
)
# use host config to access app from mobiles in the same network
shiny::runApp(appDir = app, host = '0.0.0.0')
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
I have an application, written in Sencha Touch 2.2.1, we just saw that on current Chrome (43) and on Android 4.x and 5.x some parts of the application aren't working, while on Safari on Windows, Mac and iOS everything is fine.
e.g. we have the following Code on the index.html getting called:
Ext.Viewport.items.get('main').setActiveItem(target);
Where targetis the name of the view to be loaded.
In the view we have the following code, that is not called in Chrome:
...
listeners: {
painted: function () {
this.fireEvent('isPainted');
}
},
...
The template of the view gets rendered to the html, but the listener is never called.
Any ideas on how this can be handled?
I have set up a small mobile application and during tests I have stumbled upon a problem with older versions of mobile devices running Android version 2. Please note that iPhones, iPads and newer versions of Android, namely 4.xx display the pages well. The problem is as follows:
When page is called directly from the link:
Home
it is properly displayed.
However, when there is a click handler on a link, like here:
$(document).on('click', '#lstAddrList li', function ()
{
var anchor = $(this).find('a');
sessionStorage.SiteAddr = anchor.attr('id');
changePage();
});
the list line (in this case) stays selected and nothing happens. It is ONLY after the calling page is refreshed directly from the browser when the called page is displayed. I have a feeling that older Androids do not properly handle changePage() method.
Will you have some ideas?
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);
}