package org.vaadin.touchkit.gwt.client;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.MetaElement;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
public class Ios7HomeScreenWebAppHackLoader extends TouchKitPlatformHackLoader {
private Integer bootWidth;
private Integer bootHeight;
/**
* Adds workaround for ios7 home screen web apps, that have multiple issues
* with "standard" viewport settings. Conditionally added ( deferred with n. 1 sec
* delay) absolute pixel height (copied from window.innerHeight) for
* "device-height" seems to work for most issue.
*
* @see org.vaadin.touchkit.gwt.client.TouchKitPlatformHackLoader#load()
*/
@Override
public void load() {
// Defer setting "boot values", else may be something weird in iphone
// when app is started in landscape mode
new Timer() {
@Override
public void run() {
initSizeIfNeeded();
/* "Somewhat" working viewport settings */
addHeightToViewPort();
}
}.schedule(1000);
// ... and set it each time size changes (most often orientation
// change)
Window.addResizeHandler(new ResizeHandler() {
private Timer deferredResizeHandler;
@Override
public void onResize(ResizeEvent event) {
if (deferredResizeHandler != null) {
deferredResizeHandler.cancel();
}
// Defer to get correct orientation, 1000 ms seems to be just
// enough (tested on ipad mini)
deferredResizeHandler = new Timer() {
@Override
public void run() {
if (!isVirtualKeyboardOn()) {
fixHtmlHeightToWindowInnerHeight();
} else {
deferredResizeHandler.schedule(1000);
}
}
};
deferredResizeHandler.schedule(1000);
}
});
}
private void initSizeIfNeeded() {
if (bootWidth == null) {
if (isLandscape()) {
bootWidth = getWindowInnerHeight();
bootHeight = getWindowInnerWidth();
} else {
bootHeight = getWindowInnerHeight();
bootWidth = getWindowInnerWidth();
}
// log("BS" + bootWidth + " x " + bootHeight);
}
}
private boolean isLandscape() {
switch (getOrientation()) {
case 0:
case 180:
return false;
case 90:
case -90:
default:
return true;
}
}
private boolean isVirtualKeyboardOn() {
int referenceHeight = isLandscape() ? bootWidth : bootHeight;
int differeceToStart = Math.abs(referenceHeight
- getWindowInnerHeight());
// log("IVKBON il" + isLandscape() + " wh" + getWindowInnerHeight()
// + " ww" + getWindowInnerWidth() + " bw" + bootWidth + " bh"
// + bootHeight);
// Allow small changes (~ status bar & e.g. hotspot notification)
if (differeceToStart > 100) {
return true;
}
return false;
}
/**
* Modifies viewport tag to include both width=device-width AND
* height=device-height. The latter is not generally known or used, but
* seems to prevent ios from changing screen size when virtual keyboard pops
* on. This is how it works in Safari, Android, mobile IE and in previous
* version of iOS home screen web apps.
* <p>
* Instead of "device-height" we use pixel height reported by
* window.innerHeight. On orientation changes that must be updated.
*/
private void addHeightToViewPort() {
MetaElement item = getViewportTag();
if (item != null) {
String attribute = item.getContent();
if (!attribute.contains("width")) {
attribute += ",width=device-width";
}
int viewPortHeight = getWindowInnerHeight();
if (!attribute.contains("height")) {
attribute += ",height=" + viewPortHeight;
} else {
attribute = updateViewPortHeight(attribute, viewPortHeight);
}
item.setContent(attribute);
}
}
private MetaElement getViewportTag() {
NodeList<Element> metas = Document.get().getElementsByTagName("meta");
for (int i = 0; i < metas.getLength(); i++) {
MetaElement item = metas.getItem(i).cast();
if ("viewport".equals(item.getAttribute("name"))) {
return item;
}
}
return null;
}
private void fixHtmlHeightToWindowInnerHeight() {
final MetaElement tag = getViewportTag();
int viewPortHeight = getWindowInnerHeight();
String c = updateViewPortHeight(tag.getContent(), viewPortHeight);
tag.setContent(c);
}
private static final native String updateViewPortHeight(String s, int h)
/*-{
return s.replace(/,height=\w+/,",height=" + h);
}-*/;;
private static final native int getOrientation()
/*-{
return $wnd.orientation;
}-*/;
private static native int getWindowInnerHeight()
/*-{
return $wnd.innerHeight;
}-*/;
private static native int getWindowInnerWidth()
/*-{
return $wnd.innerWidth;
}-*/;
}