/**
*
* Copyright 2008 - 2011
*
* 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.1.1
*/
package loon;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.opengles.GL10;
import loon.LGame.LMode;
import loon.action.ActionControl;
import loon.core.Assets;
import loon.core.CallQueue;
import loon.core.event.Updateable;
import loon.core.geom.RectBox;
import loon.core.graphics.Screen;
import loon.core.graphics.device.LColor;
import loon.core.graphics.device.LFont;
import loon.core.graphics.opengl.GL;
import loon.core.graphics.opengl.GLEx;
import loon.core.graphics.opengl.LSTRFont;
import loon.core.graphics.opengl.LTexture;
import loon.core.graphics.opengl.LTextures;
import loon.core.graphics.opengl.LTexture.Format;
import loon.core.processes.RealtimeProcessManager;
import loon.core.timer.LTimerContext;
import loon.core.timer.SystemTimer;
import loon.utils.MathUtils;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.opengl.GLSurfaceView.Renderer;
import android.os.SystemClock;
import android.util.Log;
import android.view.KeyEvent;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
public final class AndroidView extends CallQueue implements Renderer {
private GLMode glMode = GLMode.Default;
public static enum GLMode {
Default, VBO;
private String text;
private GLMode() {
text = "GLMode : " + name();
}
@Override
public String toString() {
return text;
};
}
private long lastTimeMicros, currTimeMicros, goalTimeMicros,
elapsedTimeMicros, remainderMicros, elapsedTime, frameCount,
frames;
private long maxFrames = LSystem.DEFAULT_MAX_FPS, frameRate;
private final Object synch = new Object();
private final LTimerContext timerContext = new LTimerContext();
private AndroidViewTools.Logo logoFlag;
private SystemTimer timer;
private LSTRFont fpsFont;
private boolean isFPS, isMemory;
private boolean onRunning, onPause, onDestroy, onResume;
private GLEx gl;
private int maxWidth, maxHeight;
private Context context;
private SurfaceView surfaceView;
private boolean supportVBO;
private int width, height;
private LProcess process;
public AndroidView(LGame activity, LMode mode, boolean landscape,
boolean fullScreen) {
this.setFPS(LSystem.DEFAULT_MAX_FPS);
this.initScreen(activity, mode, landscape, fullScreen);
this.surfaceView = createGLSurfaceView(activity);
this.process = LSystem.screenProcess;
}
private void initScreen(LGame activity, LMode mode, boolean fullScreen,
boolean landscape) {
LSystem.screenActivity = activity;
LSystem.global_queue = this;
this.context = activity.getApplicationContext();
this.setFullScreen(fullScreen);
this.setLandscape(landscape, mode);
LSystem.screenActivity.checkConfigChanges(context);
}
public boolean isScale() {
return LSystem.scaleWidth != 1 || LSystem.scaleHeight != 1;
}
protected void setLandscape(final boolean landscape, LMode mode) {
RectBox d = LSystem.screenActivity.getScreenDimension();
LSystem.SCREEN_LANDSCAPE = landscape;
this.maxWidth = (int) d.getWidth();
this.maxHeight = (int) d.getHeight();
if (landscape && (d.getWidth() > d.getHeight())) {
maxWidth = (int) d.getWidth();
maxHeight = (int) d.getHeight();
} else if (landscape && (d.getWidth() < d.getHeight())) {
maxHeight = (int) d.getWidth();
maxWidth = (int) d.getHeight();
} else if (!landscape && (d.getWidth() < d.getHeight())) {
maxWidth = (int) d.getWidth();
maxHeight = (int) d.getHeight();
} else if (!landscape && (d.getWidth() > d.getHeight())) {
maxHeight = (int) d.getWidth();
maxWidth = (int) d.getHeight();
}
if (mode != LMode.Max) {
if (landscape) {
this.width = LSystem.MAX_SCREEN_WIDTH;
this.height = LSystem.MAX_SCREEN_HEIGHT;
} else {
this.width = LSystem.MAX_SCREEN_HEIGHT;
this.height = LSystem.MAX_SCREEN_WIDTH;
}
} else {
if (landscape) {
this.width = maxWidth >= LSystem.MAX_SCREEN_WIDTH ? LSystem.MAX_SCREEN_WIDTH
: maxWidth;
this.height = maxHeight >= LSystem.MAX_SCREEN_HEIGHT ? LSystem.MAX_SCREEN_HEIGHT
: maxHeight;
} else {
this.width = maxWidth >= LSystem.MAX_SCREEN_HEIGHT ? LSystem.MAX_SCREEN_HEIGHT
: maxWidth;
this.height = maxHeight >= LSystem.MAX_SCREEN_WIDTH ? LSystem.MAX_SCREEN_WIDTH
: maxHeight;
}
}
if (mode == LMode.Fill) {
LSystem.scaleWidth = ((float) maxWidth) / width;
LSystem.scaleHeight = ((float) maxHeight) / height;
} else if (mode == LMode.FitFill) {
RectBox res = AndroidGraphicsUtils.fitLimitSize(width, height, maxWidth,
maxHeight);
maxWidth = res.width;
maxHeight = res.height;
LSystem.scaleWidth = ((float) maxWidth) / width;
LSystem.scaleHeight = ((float) maxHeight) / height;
} else if (mode == LMode.Ratio) {
maxWidth = View.MeasureSpec.getSize(maxWidth);
maxHeight = View.MeasureSpec.getSize(maxHeight);
float userAspect = (float) width / (float) height;
float realAspect = (float) maxWidth / (float) maxHeight;
if (realAspect < userAspect) {
maxHeight = Math.round(maxWidth / userAspect);
} else {
maxWidth = Math.round(maxHeight * userAspect);
}
LSystem.scaleWidth = ((float) maxWidth) / width;
LSystem.scaleHeight = ((float) maxHeight) / height;
} else if (mode == LMode.MaxRatio) {
maxWidth = View.MeasureSpec.getSize(maxWidth);
maxHeight = View.MeasureSpec.getSize(maxHeight);
float userAspect = (float) width / (float) height;
float realAspect = (float) maxWidth / (float) maxHeight;
if ((realAspect < 1 && userAspect > 1)
|| (realAspect > 1 && userAspect < 1)) {
userAspect = (float) height / (float) width;
}
if (realAspect < userAspect) {
maxHeight = Math.round(maxWidth / userAspect);
} else {
maxWidth = Math.round(maxHeight * userAspect);
}
LSystem.scaleWidth = ((float) maxWidth) / width;
LSystem.scaleHeight = ((float) maxHeight) / height;
} else {
LSystem.scaleWidth = 1;
LSystem.scaleHeight = 1;
}
if (LSystem.screenRect == null) {
LSystem.screenRect = new RectBox(0, 0, width, height);
} else {
LSystem.screenRect.setBounds(0, 0, width, height);
}
StringBuffer sbr = new StringBuffer();
sbr.append("Mode:").append(mode);
sbr.append("\nWidth:").append(width).append(",Height:" + height);
sbr.append("\nMaxWidth:").append(maxWidth)
.append(",MaxHeight:" + maxHeight);
sbr.append("\nScale:").append(isScale());
Log.i("Android2DSize", sbr.toString());
}
public int getMaxWidth() {
return maxWidth;
}
public int getMaxHeight() {
return maxHeight;
}
public Context getContext() {
return context;
}
private SurfaceView createGLSurfaceView(LGame activity) {
android.opengl.GLSurfaceView.EGLConfigChooser configChooser = getEglConfigChooser();
if (LSystem.isAndroidVersionHigher(11)) {
GLSurfaceView view = new GLSurfaceView(activity) {
@Override
public InputConnection onCreateInputConnection(
EditorInfo outAttrs) {
BaseInputConnection connection = new BaseInputConnection(
this, false) {
@Override
public boolean deleteSurroundingText(int beforeLength,
int afterLength) {
int sdkVersion = Integer
.parseInt(android.os.Build.VERSION.SDK);
if (sdkVersion >= 16) {
if (beforeLength == 1 && afterLength == 0) {
sendDownUpKeyEventForBackwardCompatibility(KeyEvent.KEYCODE_DEL);
return true;
}
}
return super.deleteSurroundingText(beforeLength,
afterLength);
}
private void sendDownUpKeyEventForBackwardCompatibility(
final int code) {
final long eventTime = SystemClock.uptimeMillis();
super.sendKeyEvent(new KeyEvent(eventTime,
eventTime, KeyEvent.ACTION_DOWN, code, 0,
0, -1, 0, KeyEvent.FLAG_SOFT_KEYBOARD
| KeyEvent.FLAG_KEEP_TOUCH_MODE));
super.sendKeyEvent(new KeyEvent(SystemClock
.uptimeMillis(), eventTime,
KeyEvent.ACTION_UP, code, 0, 0, -1, 0,
KeyEvent.FLAG_SOFT_KEYBOARD
| KeyEvent.FLAG_KEEP_TOUCH_MODE));
}
};
return connection;
}
};
if (configChooser != null) {
view.setEGLConfigChooser(configChooser);
} else {
view.setEGLConfigChooser(5, 6, 5, 0, 16, 0);
}
view.setRenderer(this);
surfaceView = view;
} else {
AndroidGLSurfaceViewCupcake viewCupcake = new AndroidGLSurfaceViewCupcake(
activity);
if (configChooser != null) {
viewCupcake.setEGLConfigChooser(configChooser);
} else {
viewCupcake.setEGLConfigChooser(5, 6, 5, 0, 16, 0);
}
viewCupcake.setRenderer(this);
surfaceView = viewCupcake;
}
try {
LSystem.screenProcess = new LProcess(surfaceView, width, height);
surfaceView.setFocusable(true);
surfaceView.setFocusableInTouchMode(true);
} catch (Exception empty) {
}
return surfaceView;
}
private android.opengl.GLSurfaceView.EGLConfigChooser getEglConfigChooser() {
if (LSystem.isSamsung7500()) {
return new android.opengl.GLSurfaceView.EGLConfigChooser() {
@Override
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
int[] attributes = new int[] { EGL10.EGL_DEPTH_SIZE, 16,
EGL10.EGL_NONE };
EGLConfig[] configs = new EGLConfig[1];
int[] result = new int[1];
egl.eglChooseConfig(display, attributes, configs, 1, result);
return configs[0];
}
};
} else {
return new AndroidEglConfigChooser(5, 6, 5, 0, 16, 0, 0);
}
}
final void resume() {
if (surfaceView == null) {
return;
}
synchronized (synch) {
LSystem.isRunning = true;
LSystem.isResume = true;
timer = LSystem.getSystemTimer();
LTextures.reload();
Assets.onResume();
}
}
final void pause() {
if (surfaceView == null) {
return;
}
synchronized (synch) {
if (!LSystem.isRunning) {
return;
}
LSystem.isRunning = false;
LSystem.isPaused = true;
Assets.onPause();
while (LSystem.isPaused) {
try {
synch.wait(4000);
} catch (InterruptedException ignored) {
}
}
}
}
final void destroy() {
if (surfaceView == null) {
return;
}
synchronized (synch) {
LSystem.isRunning = false;
LSystem.isDestroy = true;
if (LSystem.screenProcess != null) {
LSystem.screenProcess.onDestroy();
ActionControl.getInstance().stopAll();
Assets.onDestroy();
LSystem.destroy();
LSystem.gc();
}
while (LSystem.isDestroy) {
try {
synch.wait();
} catch (InterruptedException ex) {
}
}
}
}
@Override
public void onDrawFrame(javax.microedition.khronos.opengles.GL10 gl10) {
if (surfaceView == null) {
return;
}
this.onRunning = false;
this.onPause = false;
this.onDestroy = false;
this.onResume = false;
synchronized (synch) {
onRunning = LSystem.isRunning;
onPause = LSystem.isPaused;
onDestroy = LSystem.isDestroy;
onResume = LSystem.isResume;
if (LSystem.isResume) {
LSystem.isResume = false;
}
if (LSystem.isPaused) {
LSystem.isPaused = false;
synch.notifyAll();
}
if (LSystem.isDestroy) {
LSystem.isDestroy = false;
synch.notifyAll();
}
}
if (onResume) {
Log.i("Android2DView", "onResume");
timer = LSystem.getSystemTimer();
lastTimeMicros = timer.getTimeMicros();
elapsedTime = 0;
remainderMicros = 0;
process.onResume();
}
_queue.execute();
if (onRunning) {
if (LSystem.isLogo) {
synchronized (synch) {
if (logoFlag == null) {
LSystem.isLogo = false;
return;
}
logoFlag.draw(gl);
if (logoFlag.finish) {
gl.setAlpha(1.0f);
gl.setBlendMode(GL.MODE_NORMAL);
gl.drawClear();
LSystem.isLogo = false;
logoFlag.dispose();
logoFlag = null;
return;
}
}
return;
}
if (!process.next()) {
return;
}
process.load();
process.calls();
goalTimeMicros = lastTimeMicros + 1000000L / maxFrames;
currTimeMicros = timer.sleepTimeMicros(goalTimeMicros);
elapsedTimeMicros = currTimeMicros - lastTimeMicros
+ remainderMicros;
elapsedTime = MathUtils.max(0, (elapsedTimeMicros / 1000));
remainderMicros = elapsedTimeMicros - elapsedTime * 1000;
lastTimeMicros = currTimeMicros;
timerContext.millisSleepTime = remainderMicros;
timerContext.timeSinceLastUpdate = elapsedTime;
RealtimeProcessManager.get().tick(elapsedTime);
ActionControl.update(elapsedTime);
process.runTimer(timerContext);
if (LSystem.AUTO_REPAINT) {
int repaintMode = process.getRepaintMode();
switch (repaintMode) {
case Screen.SCREEN_BITMAP_REPAINT:
gl.reset(true);
if (process.getX() == 0 && process.getY() == 0) {
gl.drawTexture(process.getBackground(), 0, 0);
} else {
gl.drawTexture(process.getBackground(), process.getX(),
process.getY());
}
break;
case Screen.SCREEN_COLOR_REPAINT:
gl.reset(true);
LColor c = process.getColor();
if (c != null) {
gl.drawClear(c);
}
break;
case Screen.SCREEN_CANVAS_REPAINT:
gl.reset(true);
break;
case Screen.SCREEN_NOT_REPAINT:
gl.reset(true);
break;
default:
gl.reset(true);
if (process.getX() == 0 && process.getY() == 0) {
gl.drawTexture(
process.getBackground(),
repaintMode / 2
- LSystem.random.nextInt(repaintMode),
repaintMode / 2
- LSystem.random.nextInt(repaintMode));
} else {
gl.drawTexture(process.getBackground(),
process.getX() + repaintMode / 2
- LSystem.random.nextInt(repaintMode),
process.getY() + repaintMode / 2
- LSystem.random.nextInt(repaintMode));
}
break;
}
gl.resetFont();
process.draw(gl);
process.drawable(elapsedTime);
if (isFPS) {
tickFrames();
fpsFont.drawString("FPS:" + frameRate, 5, 5, 0,
LColor.white);
}
if (isMemory) {
Runtime runtime = Runtime.getRuntime();
long totalMemory = runtime.totalMemory();
long currentMemory = totalMemory - runtime.freeMemory();
String memory = ((float) ((currentMemory * 10) >> 20) / 10)
+ " of "
+ ((float) ((totalMemory * 10) >> 20) / 10) + " MB";
fpsFont.drawString("MEMORY:" + memory, 5, 25, 0,
LColor.white);
}
process.drawEmulator(gl);
process.unload();
}
}
if (onPause) {
Log.i("Android2DView", "onPause");
pause(500);
process.onPause();
}
if (onDestroy) {
Log.i("Android2DView", "onDestroy");
if (process != null) {
process.end();
}
process.onDestroy();
}
}
@Override
public void invokeAsync(final Updateable act) {
LSystem.getOSHandler().post(new Runnable() {
@Override
public void run() {
act.action(null);
}
});
}
private final void pause(long sleep) {
try {
Thread.sleep(sleep);
} catch (InterruptedException ex) {
}
}
private void tickFrames() {
long time = System.currentTimeMillis();
if (time - frameCount > 1000L) {
frameRate = Math.min(maxFrames, frames);
frames = 0;
frameCount = time;
}
frames++;
}
@Override
public void onSurfaceChanged(GL10 gl10, int width, int height) {
if (surfaceView == null) {
return;
}
if (gl != null) {
Log.i("Android2DView", "onSurfaceChanged");
this.width = (int) (width / LSystem.scaleWidth);
this.height = (int) (height / LSystem.scaleHeight);
gl.setViewPort(0, 0, width, height);
if (!LSystem.isCreated) {
if (process != null) {
process.begin();
}
LSystem.isCreated = true;
synchronized (this) {
LSystem.isRunning = true;
}
}
if (process != null) {
process.resize(this.width, this.height);
}
} else if (gl == null || !gl.equals(gl10, width, height)) {
createGL(gl10);
}
}
@Override
public void onSurfaceCreated(GL10 gl10, EGLConfig config) {
createGL(gl10);
}
private void createGL(GL10 gl10) {
if (surfaceView == null) {
return;
}
if (gl == null || !gl.equals(gl10, width, height)) {
Log.i("Android2DView", "onSurfaceCreated");
this.gl = new GLEx(gl10, LSystem.screenRect.width,
LSystem.screenRect.height);
this.supportVBO = GLEx.checkVBO();
if (glMode == GLMode.VBO) {
if (supportVBO) {
GLEx.setVbo(true);
} else {
GLEx.setVbo(false);
setGLMode(GLMode.Default);
}
} else {
GLEx.setVbo(false);
setGLMode(GLMode.Default);
}
RectBox rect = LSystem.screenActivity.getScreenDimension();
gl.update();
gl.setViewPort(0, 0, rect.width, rect.height);
}
}
public void setFullScreen(boolean fullScreen) {
Window win = LSystem.screenActivity.getWindow();
if (LSystem.isAndroidVersionHigher(11)) {
int flagHardwareAccelerated = 0x1000000;
win.setFlags(flagHardwareAccelerated, flagHardwareAccelerated);
}
if (fullScreen) {
win.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
win.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
win.requestFeature(android.view.Window.FEATURE_NO_TITLE);
} else {
win.setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
}
}
public void setGLMode(GLMode mode) {
this.glMode = mode;
}
public boolean isSupportVBO() {
return supportVBO;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public LTexture getLogo() {
return logoFlag.logo;
}
public void setLogo(LTexture img) {
if (logoFlag == null) {
this.logoFlag = new AndroidViewTools.Logo(img);
}
}
public void setLogo(String path) {
setLogo(LTextures.loadTexture(path, Format.BILINEAR));
}
public void setShowLogo(boolean showLogo) {
LSystem.isLogo = showLogo;
if (logoFlag == null) {
setLogo(LSystem.FRAMEWORK_IMG_NAME + "logo.png");
}
}
private final String pFontString = " MEORYFPSB0123456789:.of";
public void setShowFPS(boolean showFps) {
this.isFPS = showFps;
if (showFps && fpsFont == null) {
this.fpsFont = new LSTRFont(LFont.getDefaultFont(), pFontString);
}
}
public void setShowMemory(boolean showMemory) {
this.isMemory = showMemory;
if (showMemory && fpsFont == null) {
this.fpsFont = new LSTRFont(LFont.getDefaultFont(), pFontString);
}
}
public void setFPS(long frames) {
this.maxFrames = frames;
}
public long getMaxFPS() {
return this.maxFrames;
}
public long getCurrentFPS() {
return this.frameRate;
}
public float getScalex() {
return LSystem.scaleWidth;
}
public float getScaley() {
return LSystem.scaleHeight;
}
public View getView() {
return surfaceView;
}
public boolean isRunning() {
return onRunning;
}
public boolean isPause() {
return onPause;
}
public boolean isResume() {
return onResume;
}
public boolean isDestroy() {
return onDestroy;
}
}