/**
* Copyright 2008 - 2015 The Loon Game Engine Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* @project loon
* @author cping
* @email:javachenpeng@yahoo.com
* @version 0.5
*/
package loon.html5.gwt;
import java.util.HashMap;
import loon.LGame;
import loon.LSetting;
import loon.LSystem;
import loon.LazyLoading;
import loon.Platform;
import loon.event.KeyMake;
import loon.event.SysInput;
import loon.event.Updateable;
import loon.html5.gwt.GWTGame.GWTSetting;
import loon.html5.gwt.GWTGame.Repaint;
import loon.html5.gwt.soundmanager2.SoundManager;
import loon.html5.gwt.preloader.LocalAssetResources;
import loon.html5.gwt.preloader.Preloader;
import loon.html5.gwt.preloader.Preloader.PreloaderCallback;
import loon.html5.gwt.preloader.Preloader.PreloaderState;
import loon.utils.MathUtils;
import com.google.gwt.core.client.Callback;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.ScriptInjector;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
public abstract class Loon implements Platform, EntryPoint, LazyLoading {
private static String cur_language = null;
private static String cur_browserType = null;
private HashMap<String, OrientationChangedHandler> _handlers = new HashMap<String, OrientationChangedHandler>();
private Orientation _orientation;
private int _currentHandlerId = 1;
static final class JsMap extends JavaScriptObject {
protected JsMap() {
}
public native String getKey() /*-{
return this["k"];
}-*/;
public native String getValue() /*-{
return this["v"];
}-*/;
}
public interface OrientationChangedEvent {
void onOrientationChanged();
}
public interface LoadingListener {
public void beforeSetup();
public void afterSetup();
}
public interface OrientationChangedHandler {
void onChanged(Orientation newOrientation);
}
protected LoadingListener loadingListener;
protected Preloader preloader;
protected Panel root;
protected static Loon self;
private LazyLoading.Data mainData = null;
protected GWTResources resources = null;
protected GWTProgress progress = null;
LSetting setting = null;
GWTSetting config = null;
public String getBaseUrl() {
return preloader.baseUrl;
}
public Preloader getPreloader() {
return preloader;
}
@Override
public void onModuleLoad() {
initTime();
initRequestAnimFrame();
Loon.self = this;
_orientation = calculateScreenOrientation();
try {
this.registerOrientationChangedHandler(new OrientationChangedEvent() {
@Override
public void onOrientationChanged() {
_orientation = calculateScreenOrientation();
for (OrientationChangedHandler handler : _handlers.values()) {
handler.onChanged(getOrientation());
}
}
});
} catch (Exception e) {
consoleLog("Does not support gets screen orientation .");
_orientation = Orientation.Landscape;
}
this.onMain();
if (this.setting instanceof GWTSetting) {
config = (GWTSetting) this.setting;
} else {
config = new GWTSetting();
config.copy(this.setting);
}
this.setting = config;
// gwt中提供的AnimationScheduler默认不能设置间隔,所以转个能设置的默认(走setTimeout)
if (config.fps != 60 && config.repaint == Repaint.AnimationScheduler) {
config.repaint = Repaint.RequestAnimationFrame;
}
this.progress = config.progress;
if (this.progress == null) {
boolean flag = (config.internalRes == null && !config.jsloadRes);
if (flag) {
int rad = MathUtils.random(0, 1);
switch (rad) {
case 0:
this.progress = GWTProgressDef.newSimpleLogoProcess(config);
break;
case 1:
default:
this.progress = GWTProgressDef.newLogoProcess();
break;
}
} else {
this.progress = GWTProgressDef.newSimpleLogoProcess(config);
}
}
Element element = Document.get().getElementById(config.rootId);
if (element == null) {
VerticalPanel panel = new VerticalPanel();
panel.setWidth("" + config.width + "px");
panel.setHeight("" + config.height + "px");
panel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
panel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
RootPanel.get().add(panel);
RootPanel.get().setWidth("" + config.width + "px");
RootPanel.get().setHeight("" + config.height + "px");
this.root = panel;
} else {
VerticalPanel panel = new VerticalPanel();
panel.setWidth("" + config.width + "px");
panel.setHeight("" + config.height + "px");
panel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
panel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
element.appendChild(panel.getElement());
this.root = panel;
}
SoundManager.init(GWT.getModuleBaseURL(), 9, config.preferFlash,
new SoundManager.SoundManagerCallback() {
@Override
public void onready() {
loadResources();
}
@Override
public void ontimeout(String status, String errorType) {
consoleLog("SoundManager:" + status + " " + errorType);
// 音频环境目前浏览器支持有限,并非所有浏览器都完美支持音频播放,但是——
// 无论音频载入成败与否,都尝试加载其它资源。
loadResources();
}
});
}
private final void loadResources() {
boolean internalExist = config.internalRes != null;
boolean jsres = config.jsloadRes || internalExist;
if (jsres) {
final LocalAssetResources localRes = internalExist ? config.internalRes
: new LocalAssetResources();
ScriptInjector.fromUrl("assets/resources.js")
.setCallback(new Callback<Void, Exception>() {
public void onFailure(Exception reason) {
consoleLog("resources script load failed.");
}
public void onSuccess(Void result) {
JsArray<JsMap> list = loadJavaScriptResources(
localRes).cast();
int size = list.length();
for (int i = 0; i < size; i++) {
JsMap res = list.get(i);
String key = res.getKey();
String value = res.getValue();
String ext = LSystem.getExtension(key);
if (LSystem.isText(ext)) {
localRes.putText(key, value);
} else if (LSystem.isImage(ext)) {
localRes.putImage(key, value);
} else if (LSystem.isAudio(ext)) {
// noop
} else {
localRes.putBlobString(key, value);
}
}
localRes.commit();
loadResources(progress.getPreloaderCallback(
Loon.self, root), localRes);
}
}).setWindow(ScriptInjector.TOP_WINDOW).inject();
} else {
loadResources(progress.getPreloaderCallback(Loon.self, root), null);
}
}
private native JsArray<JavaScriptObject> loadJavaScriptResources(
LocalAssetResources res) /*-{
return new $wnd.LocalResources().running(res);
}-*/;
Preloader loadResources(final PreloaderCallback callback,
final LocalAssetResources localRes) {
this.preloader = createPreloader(localRes);
this.preloader.preload("assets.txt", new PreloaderCallback() {
@Override
public void error(String file) {
callback.error(file);
}
@Override
public void update(PreloaderState state) {
callback.update(state);
if (state.hasEnded()) {
getRootPanel().clear();
if (loadingListener != null) {
loadingListener.beforeSetup();
}
mainLoop();
if (loadingListener != null) {
loadingListener.afterSetup();
}
}
}
});
return this.preloader;
}
void mainLoop() {
this.resources = new GWTResources(preloader);
this.createGame().start();
}
public abstract void onMain();
public String getPreloaderBaseURL() {
return GWT.getHostPageBaseURL() + "assets/";
}
public Preloader createPreloader(LocalAssetResources res) {
return new Preloader(getPreloaderBaseURL(), res);
}
private static native void initTime()
/*-{
Date.now = Date.now || function() {
return new Date().getTime();
};
window.performance = window.performance || {};
performance.now = (function() {
return performance.now || performance.mozNow || performance.msNow
|| performance.oNow || performance.webkitNow || Date.now;
})();
}-*/;
private GWTGame game;
public Panel getRootPanel() {
return root;
}
protected GWTGame createGame() {
return this.game = new GWTGame(this, root, config);
}
public LGame getGame() {
return game;
}
protected GWTGame initialize() {
if (game != null) {
game.register(mainData.onScreen());
}
return game;
}
public void register(LSetting s, LazyLoading.Data data) {
this.setting = s;
this.mainData = data;
}
public int getContainerWidth() {
int width = getJSNIAvailWidth() <= 0 ? getJSNIScreenWidth()
: getJSNIAvailWidth();
if (isIOS() && isLandscape()) {
return getContainerHeight();
}
if (isNextGenerationIos()) {
return width * 2;
}
return width;
}
public int getContainerHeight() {
int height = getJSNIAvailHeight() <= 0 ? getJSNIScreenHeight()
: getJSNIAvailHeight();
if (isIOS() && isLandscape()) {
return getContainerWidth();
}
if (isNextGenerationIos()) {
return height * 2;
}
return height;
}
public Orientation getOrientation() {
if (_orientation == Orientation.Landscape) {
return Orientation.Landscape;
}
if (getJSNIScreenHeight() > getJSNIScreenWidth()) {
return Orientation.Portrait;
} else {
return Orientation.Landscape;
}
}
public void close() {
closeImpl();
}
private static native void closeImpl()
/*-{
$wnd.close();
}-*/;
public native static void consoleLog(String message) /*-{
console.log("GWT: " + message);
}-*/;
@Override
public void sysText(final SysInput.TextEvent event,
final KeyMake.TextType textType, final String label,
final String initVal) {
if (game == null) {
event.cancel();
return;
}
LSystem.load(new Updateable() {
@Override
public void action(Object a) {
String result = Window.prompt(label, initVal);
if (game.input() instanceof GWTInputMake) {
((GWTInputMake) game.input()).emitFakeMouseUp();
}
if (result != null) {
event.input(result);
} else {
event.cancel();
}
}
});
}
@Override
public void sysDialog(final SysInput.ClickEvent event, final String title,
final String text, final String ok, final String cancel) {
if (game == null) {
event.cancel();
return;
}
LSystem.load(new Updateable() {
@Override
public void action(Object a) {
boolean result;
if (cancel != null)
result = Window.confirm(text);
else {
Window.alert(text);
result = true;
}
if (game.input() instanceof GWTInputMake) {
((GWTInputMake) game.input()).emitFakeMouseUp();
}
if (result) {
event.clicked();
} else {
event.cancel();
}
}
});
}
private native void registerOrientationChangedHandler(
OrientationChangedEvent handler) /*-{
var callback = function() {
handler.@loon.html5.gwt.Loon.OrientationChangedEvent::onOrientationChanged()();
}
$wnd.addEventListener("orientationchange", callback, false);
}-*/;
public boolean isMobile() {
return isAndroid() || isIOS() || isBlackBerry()
|| getUserAgent().contains("mobile");
}
/**
* 通过orientation属性取得屏幕是否竖屏,若浏览器不支持orientation,有可能取不到
*
* @return
*/
protected native boolean isPortrait() /*-{
var result = false;
if ($wnd.orientation != null && $wnd.orientation == 0) {
result = true;
}
return result;
}-*/;
/**
* 通过orientation属性取得屏幕是否横屏,若浏览器不支持orientation,有可能取不到
*
* @return
*/
protected native boolean isLandscape() /*-{
var result = false;
if ($wnd.orientation != null
&& ($wnd.orientation == 90 || orientation == -90)) {
result = true;
}
return result;
}-*/;
/**
* 若同时取不到横竖,则判定为不支持
*/
public boolean noSupportOrientation() {
return !isPortrait() && !isLandscape();
}
public native float getOrientationValue() /*-{
return $wnd.orientation || 0;
}-*/;
private Orientation calculateScreenOrientation() {
return isPortrait() ? Orientation.Portrait : Orientation.Landscape;
}
public String addHandler(OrientationChangedHandler handler) {
int newHandlerIdValue = _currentHandlerId++;
String newHandlerId = String.valueOf(newHandlerIdValue);
_handlers.put(newHandlerId, handler);
return newHandlerId;
}
/**
* 屏幕[完整宽度]
*
* @return
*/
protected native int getJSNIScreenWidth() /*-{
return $wnd.screen.width || 0;
}-*/;
/**
* 屏幕[完整高度]
*
* @return
*/
protected native int getJSNIScreenHeight() /*-{
return $wnd.screen.height || 0;
}-*/;
/**
* 屏幕[可用宽度]
*
* @return
*/
protected native int getJSNIAvailWidth() /*-{
return $wnd.screen.availWidth || 0;
}-*/;
/**
* 屏幕[可用高度]
*
* @return
*/
protected native int getJSNIAvailHeight() /*-{
return $wnd.screen.availHeight || 0;
}-*/;
public native static float devicePixelRatio() /*-{
return $wnd.devicePixelRatio || 1;
}-*/;
public native static float backingStorePixelRatio() /*-{
return $wnd.webkitBackingStorePixelRatio || 1;
}-*/;
public boolean supportsDisplayModeChange() {
return supportsFullscreenJSNI();
}
private native boolean supportsFullscreenJSNI() /*-{
if ("fullscreenEnabled" in $doc) {
return $doc.fullscreenEnabled;
}
if ("webkitFullscreenEnabled" in $doc) {
return $doc.webkitFullscreenEnabled;
}
if ("mozFullScreenEnabled" in $doc) {
return $doc.mozFullScreenEnabled;
}
if ("msFullscreenEnabled" in $doc) {
return $doc.msFullscreenEnabled;
}
return false;
}-*/;
public boolean isFullscreen() {
return isFullscreenJSNI();
}
private native boolean isFullscreenJSNI() /*-{
if ("fullscreenElement" in $doc) {
return $doc.fullscreenElement != null;
}
if ("msFullscreenElement" in $doc) {
return $doc.msFullscreenElement != null;
}
if ("webkitFullscreenElement" in $doc) {
return $doc.webkitFullscreenElement != null;
}
if ("mozFullScreenElement" in $doc) {
return $doc.mozFullScreenElement != null;
}
if ("webkitIsFullScreen" in $doc) {
return $doc.webkitIsFullScreen;
}
if ("mozFullScreen" in $doc) {
return $doc.mozFullScreen;
}
return false
}-*/;
public boolean isHdpi() {
return devicePixelRatio() == 1.5;
}
public boolean isXhdpi() {
return devicePixelRatio() > 1.5;
}
public native String getUserAgent() /*-{
return $wnd.navigator.userAgent.toLowerCase();
}-*/;
public boolean isAndroid() {
return isAndroidPhone() || isAndroidTablet();
}
public boolean isIPhone() {
String userAgent = getUserAgent();
if (userAgent.contains("iphone") && devicePixelRatio() < 2) {
return true;
}
return false;
}
public boolean isIPad() {
String userAgent = getUserAgent();
if (userAgent.contains("ipad") && devicePixelRatio() < 2) {
return true;
}
return false;
}
public boolean isIOS() {
return isIPad() || isIPadRetina() || isIPhone() || isRetina();
}
public boolean isRetina() {
String userAgent = getUserAgent();
if (userAgent.contains("iphone") && devicePixelRatio() >= 2) {
return true;
}
return false;
}
public boolean isIPadRetina() {
String userAgent = getUserAgent();
if (userAgent.contains("ipad") && devicePixelRatio() >= 2) {
return true;
}
return false;
}
public boolean isDesktop() {
return !isIOS() && !isAndroid() && !isBlackBerry()
&& !getUserAgent().contains("mobile");
}
public boolean isTablet() {
return isIPad() || isIPadRetina() || isAndroidTablet();
}
public boolean isAndroidTablet() {
String userAgent = getUserAgent();
if (userAgent.contains("android") && !userAgent.contains("mobile")) {
return true;
}
return false;
}
public boolean isAndroidPhone() {
String userAgent = getUserAgent();
if (userAgent.contains("android") && userAgent.contains("mobile")) {
return true;
}
return false;
}
public boolean isPhone() {
return isIPhone() || isRetina() || isAndroidPhone();
}
public boolean isBlackBerry() {
String userAgent = getUserAgent();
if (userAgent.contains("blackberry")) {
return true;
}
return false;
}
public boolean isAndroid4_4_OrHigher() {
String userAgent = getUserAgent();
if (userAgent.contains("android") && userAgent.contains("chrome")) {
return true;
}
return false;
}
public boolean isAndroid2x() {
String userAgent = getUserAgent();
if (userAgent.contains("android 2.")) {
return true;
}
return false;
}
public boolean isAndroid4_3_orLower() {
if (isAndroid4_4_OrHigher()) {
return false;
}
String userAgent = getUserAgent();
if (userAgent.contains("android")) {
return true;
}
return false;
}
public boolean isIOS6() {
if (!isIOS()) {
return false;
}
String userAgent = getUserAgent();
if (userAgent.contains("os 6_")) {
return true;
}
return false;
}
public boolean isIOS9() {
if (!isIOS()) {
return false;
}
String userAgent = getUserAgent();
if (userAgent.contains("os 9_")) {
return true;
}
return false;
}
public boolean isNextGenerationIos() {
boolean ratio = (devicePixelRatio() > 1);
return isIOS() && ratio;
}
public native boolean isStandalone() /*-{
return $wnd.navigator.standalone;
}-*/;
private static native void initRequestAnimFrame()
/*-{
$wnd.requestAnimFrame = (function() {
return window.requestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.mozRequestAnimationFrame
|| window.oRequestAnimationFrame
|| window.msRequestAnimationFrame
|| function stTime(callback, element) {
$wnd.setTimeout(callback, 16);
};
})();
$wnd.cancelAnimationFrame = (function() {
return $wnd.cancelAnimationFrame = $wnd.cancelAnimationFrame
|| $wnd.cancelRequestAnimationFrame
|| $wnd.msCancelRequestAnimationFrame
|| $wnd.mozCancelRequestAnimationFrame
|| $wnd.oCancelRequestAnimationFrame
|| $wnd.webkitCancelRequestAnimationFrame
|| $wnd.msCancelAnimationFrame
|| $wnd.mozCancelAnimationFrame
|| $wnd.webkitCancelAnimationFrame
|| $wnd.oCancelAnimationFrame || function etime(id) {
$wnd.clearTimeout(id);
};
})();
}-*/;
private static native String languageImpl()
/*-{
var nav = $wnd.navigator;
var curLanguage = nav.language;
curLanguage = curLanguage ? curLanguage : nav.browserLanguage;
curLanguage = curLanguage ? curLanguage.split("-")[0] : "en";
return curLanguage;
}-*/;
public static String language() {
if (cur_language == null) {
cur_language = languageImpl();
}
return cur_language;
}
private static native String browserTypeImpl()
/*-{
var ua = $wnd.navigator.userAgent;
var BROWSER_TYPE_WECHAT = "wechat";
var BROWSER_TYPE_ANDROID = "androidbrowser";
var BROWSER_TYPE_IE = "ie";
var BROWSER_TYPE_360 = "360browser";
var BROWSER_TYPE_MAXTHON = "maxthon";
var BROWSER_TYPE_OPERA = "opera";
var BROWSER_TYPE_UNKNOWN = "unknown";
var typeReg1 = /sogou|qzone|liebao|micromessenger|ucbrowser|360 aphone|360browser|baiduboxapp|baidubrowser|maxthon|mxbrowser|trident|miuibrowser/i;
var typeReg2 = /qqbrowser|chrome|safari|firefox|opr|oupeng|opera/i;
var browserTypes = typeReg1.exec(ua);
if (!browserTypes) {
browserTypes = typeReg2.exec(ua);
}
var browserType = browserTypes ? browserTypes[0] : BROWSER_TYPE_UNKNOWN;
if (browserType === "micromessenger") {
browserType = BROWSER_TYPE_WECHAT;
} else if (browserType === "safari"
&& (ua.match(/android.*applewebkit/))) {
browserType = BROWSER_TYPE_ANDROID;
} else if (browserType === "trident") {
browserType = BROWSER_TYPE_IE;
} else if (browserType === "360 aphone") {
browserType = BROWSER_TYPE_360;
} else if (browserType === "mxbrowser") {
browserType = BROWSER_TYPE_MAXTHON;
} else if (browserType === "opr") {
browserType = BROWSER_TYPE_OPERA;
}
return browserType;
}-*/;
public static String browserType() {
if (cur_browserType == null) {
cur_browserType = browserTypeImpl();
}
return cur_browserType;
}
}