Testing a Progressive Web Application.
When I start the app in airplane mode, I get an unexpected startup/splash experience (Android/Chrome).
Launch from Home Screen Experience
I see a white screen, followed by a brief flash of the "offline dinosaur" before the app successfully starts and all is well. The startup time is longer than I expected, especially after testing with Chrome Devtools on a laptop, where startup is near instant.
Since it is a little tricky to debug where this time is being spent (especially in the “service-worker-not-running” case), it would be helpful to have some baseline knowledge:
Launch from Browser Experience
Just a brief flash of the "offline dinosaur" before the app successfully starts. Starts much faster.
Questions
What is the expected startup time and experience on Android/Chrome?
Is the experience described above just the current state of things (11/2015)?
Is there any way to specify the startup (splash) experience for Chrome? (I'm aware of background color and 144x144 icon in app manifest for splash, but for Opera only)
First time PWA for me, so any information on this would be helpful.
My platform:
Samsung GS 5,
Android 5.0,
Chrome 46.0.2490.76
The reason for the existence of the splash screen is because on mobile it can take over a second to start the render process so we paint something (the background colour and icons) util you have a first paint generated by your app.
If you are seeing a white screen on startup it might be because you added to the homescreen prior to Chrome landing (46) the splash screen feature. Some things to lookout for:
Ensure your manifest has a short_name and name
Ensure your start_url is in the same scope as a SW that is registered on the page
Have good icons in the manifest ideally > 192px
Set background_color in the manifest to the color of your background on the page (ideally.) This will ensure that the splash screen is the expected colour of your site.
You shouldn't see the offline dinosaur at all, even when you are in aeroplane mode. Airhorner should represent the ideal experience: Blue splash screen with an icon that morphs into the display of the app.
re: Icons - I recommend actually 192px icon or higher.
Regarding the offline-dino flash:
I was using sw-toolbox and performing asynchronous work to setup route handlers at worker startup. This caused an offline dino flash when the application was offline and starting up.
To avoid that, set a sw-toolbox default handler that waits for the asynchronous route handler setup to complete.
var toolbox = require('sw-toolbox');
var setupPromise = someAsyncHandlerSetup()
.then(function () {
// make default handler temporary, allows other fetch handlers (like sw-precache, for example)
toolbox.router.default = null;
});
// until the async handler setup is done, provide a default handler
// to avoid an offline-dino flash when starting up while offline.
toolbox.router.default = function defaultHandler (request) {
return setupPromise.then(function () {
var handler = toolbox.router.match(request);
if (handler) {
return handler(request);
}
throw new Error('default handler could not handle ' + request.url);
});
};
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')
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
I create a web page(chrome & safari) for mobiles (iphone & android), I want to lock the screen orientation in portrait mode.
Unlike mobile apps,there is no manifest file and activity as it a web page.
How to lock the orientation in mobiles using technologies (css/javascript/bootstrap/jquery) or any other?
I use a manifest file for my web app, which locks orientation for Chrome on my Android. For whatever reason, Safari gives their users the "right" to do this, but not the designers of the web app... Sort of feels like copyright infringement or something! ;) Don't get me started on Safari's disgraceful rewriting/rendering of input buttons!...
Anyways, back to the answer.
1) Include a link to your manifest within the head section of your page:
<link rel="manifest" href="http://yoursite.com/manifest.json">
2) Create your manifest file, "manifest.json"
{
"name":"A nice title for your web app",
"display":"standalone",
"orientation":"portrait"
}
3) Read more about manifests HERE
From my tests, assigning the screen.lockOrientation ( every browser versions ) to a var throw an illegal invocation error. Just use wind.screen.orientation.lock('landscape'); . It
EDIT: You can't use lock orientation on safari, cause it doesn't support fullscreen api at the moment http://caniuse.com/#feat=fullscreen . The lock orientation API NEED a fullscreen page to work. In Chrome, the window.screen.orientation.lock return a promise. So, AFTER you go fullscreen with the page, you can do something like this :
var lockFunction = window.screen.orientation.lock;
if (lockFunction.call(window.screen.orientation, 'landscape')) {
console.log('Orientation locked')
} else {
console.error('There was a problem in locking the orientation')
}
However, the lock orientation and fullscreen API are still experimental, not all browsers supports it.
The lockOrientation method locks the screen into the specified orientation.
lockedAllowed = window.screen.lockOrientation(orientation);
From the following code, you can check that orientation is locked or not.
var lockOrientation = screen.lockOrientation || screen.mozLockOrientation || screen.msLockOrientation;
if (lockOrientation("landscape-primary")) {
// orientation was locked
} else {
// orientation lock failed
}
see the following link, you will get idea from this.
https://developer.mozilla.org/en-US/docs/Web/API/Screen.lockOrientation
You can use:
screen.addEventListener("orientationchange", function () {
console.log("The orientation of the screen is: " + screen.orientation);
});
and
screen.lockOrientation('landscape');
Following: https://developer.mozilla.org/en-US/docs/Web/API/CSS_Object_Model/Managing_screen_orientation
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);
}