[UI] Android surface [skip appveyor]
This commit is contained in:
@@ -81,4 +81,8 @@ android {
|
||||
path file('../../../build/xenia.wks.Android.mk')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.jetbrains:annotations:15.0'
|
||||
}
|
||||
@@ -29,7 +29,9 @@
|
||||
android:supportsRtl="true"
|
||||
android:theme="@android:style/Theme.Material.Light">
|
||||
|
||||
<activity android:name="jp.xenia.emulator.WindowDemoActivity">
|
||||
<activity
|
||||
android:name="jp.xenia.emulator.WindowDemoActivity"
|
||||
android:label="@string/activity_label_window_demo">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
|
||||
@@ -7,15 +7,15 @@ public class XeniaRuntimeException extends RuntimeException {
|
||||
public XeniaRuntimeException() {
|
||||
}
|
||||
|
||||
public XeniaRuntimeException(String name) {
|
||||
public XeniaRuntimeException(final String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
public XeniaRuntimeException(String name, Throwable cause) {
|
||||
public XeniaRuntimeException(final String name, final Throwable cause) {
|
||||
super(name, cause);
|
||||
}
|
||||
|
||||
public XeniaRuntimeException(Exception cause) {
|
||||
public XeniaRuntimeException(final Exception cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
package jp.xenia.emulator;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
public class WindowDemoActivity extends WindowedAppActivity {
|
||||
@Override
|
||||
protected String getWindowedAppIdentifier() {
|
||||
return "xenia_ui_window_vulkan_demo";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_window_demo);
|
||||
setWindowSurfaceView(findViewById(R.id.window_demo_surface_view));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package jp.xenia.emulator;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.SurfaceView;
|
||||
|
||||
public class WindowSurfaceView extends SurfaceView {
|
||||
public WindowSurfaceView(final Context context) {
|
||||
super(context);
|
||||
// Native drawing is invoked from onDraw.
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
public WindowSurfaceView(final Context context, final AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
public WindowSurfaceView(
|
||||
final Context context, final AttributeSet attrs, final int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
public WindowSurfaceView(
|
||||
final Context context, final AttributeSet attrs, final int defStyleAttr,
|
||||
final int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(final Canvas canvas) {
|
||||
final Context context = getContext();
|
||||
if (!(context instanceof WindowedAppActivity)) {
|
||||
return;
|
||||
}
|
||||
final WindowedAppActivity activity = (WindowedAppActivity) context;
|
||||
activity.onWindowSurfaceDraw(false);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,11 @@ package jp.xenia.emulator;
|
||||
import android.app.Activity;
|
||||
import android.content.res.AssetManager;
|
||||
import android.os.Bundle;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.View;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jp.xenia.XeniaRuntimeException;
|
||||
|
||||
@@ -12,21 +17,87 @@ public abstract class WindowedAppActivity extends Activity {
|
||||
System.loadLibrary("xenia-ui-window-vulkan-demo");
|
||||
}
|
||||
|
||||
private long mAppContext;
|
||||
private final WindowSurfaceOnLayoutChangeListener mWindowSurfaceOnLayoutChangeListener =
|
||||
new WindowSurfaceOnLayoutChangeListener();
|
||||
private final WindowSurfaceHolderCallback mWindowSurfaceHolderCallback =
|
||||
new WindowSurfaceHolderCallback();
|
||||
|
||||
private native long initializeWindowedAppOnCreateNative(
|
||||
// May be 0 while destroying (mainly while the superclass is).
|
||||
private long mAppContext = 0;
|
||||
|
||||
@Nullable
|
||||
private WindowSurfaceView mWindowSurfaceView = null;
|
||||
|
||||
private native long initializeWindowedAppOnCreate(
|
||||
String windowedAppIdentifier, AssetManager assetManager);
|
||||
|
||||
private native void onDestroyNative(long appContext);
|
||||
|
||||
private native void onWindowSurfaceLayoutChange(
|
||||
long appContext, int left, int top, int right, int bottom);
|
||||
|
||||
private native void onWindowSurfaceChanged(long appContext, Surface windowSurface);
|
||||
|
||||
private native void paintWindow(long appContext, boolean forcePaint);
|
||||
|
||||
protected abstract String getWindowedAppIdentifier();
|
||||
|
||||
protected void setWindowSurfaceView(@Nullable final WindowSurfaceView windowSurfaceView) {
|
||||
if (mWindowSurfaceView == windowSurfaceView) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Detach from the old surface.
|
||||
if (mWindowSurfaceView != null) {
|
||||
mWindowSurfaceView.getHolder().removeCallback(mWindowSurfaceHolderCallback);
|
||||
mWindowSurfaceView.removeOnLayoutChangeListener(mWindowSurfaceOnLayoutChangeListener);
|
||||
mWindowSurfaceView = null;
|
||||
if (mAppContext != 0) {
|
||||
onWindowSurfaceChanged(mAppContext, null);
|
||||
}
|
||||
}
|
||||
|
||||
if (windowSurfaceView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mWindowSurfaceView = windowSurfaceView;
|
||||
// The native window code assumes that, when the surface exists, it covers the entire
|
||||
// window.
|
||||
// FIXME(Triang3l): This doesn't work if the layout has already been performed.
|
||||
mWindowSurfaceView.addOnLayoutChangeListener(mWindowSurfaceOnLayoutChangeListener);
|
||||
final SurfaceHolder windowSurfaceHolder = mWindowSurfaceView.getHolder();
|
||||
windowSurfaceHolder.addCallback(mWindowSurfaceHolderCallback);
|
||||
// If setting after the creation of the surface.
|
||||
if (mAppContext != 0) {
|
||||
final Surface windowSurface = windowSurfaceHolder.getSurface();
|
||||
if (windowSurface != null) {
|
||||
onWindowSurfaceChanged(mAppContext, windowSurface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onWindowSurfaceDraw(final boolean forcePaint) {
|
||||
if (mAppContext == 0) {
|
||||
return;
|
||||
}
|
||||
paintWindow(mAppContext, forcePaint);
|
||||
}
|
||||
|
||||
// Used from the native WindowedAppContext. May be called from non-UI threads.
|
||||
protected void postInvalidateWindowSurface() {
|
||||
if (mWindowSurfaceView == null) {
|
||||
return;
|
||||
}
|
||||
mWindowSurfaceView.postInvalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
protected void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
final String windowedAppIdentifier = getWindowedAppIdentifier();
|
||||
mAppContext = initializeWindowedAppOnCreateNative(windowedAppIdentifier, getAssets());
|
||||
mAppContext = initializeWindowedAppOnCreate(windowedAppIdentifier, getAssets());
|
||||
if (mAppContext == 0) {
|
||||
finish();
|
||||
throw new XeniaRuntimeException(
|
||||
@@ -36,10 +107,54 @@ public abstract class WindowedAppActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
setWindowSurfaceView(null);
|
||||
if (mAppContext != 0) {
|
||||
onDestroyNative(mAppContext);
|
||||
}
|
||||
mAppContext = 0;
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
private class WindowSurfaceOnLayoutChangeListener implements View.OnLayoutChangeListener {
|
||||
@Override
|
||||
public void onLayoutChange(
|
||||
final View v, final int left, final int top, final int right, final int bottom,
|
||||
final int oldLeft, final int oldTop, final int oldRight, final int oldBottom) {
|
||||
if (mAppContext != 0) {
|
||||
onWindowSurfaceLayoutChange(mAppContext, left, top, right, bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class WindowSurfaceHolderCallback implements SurfaceHolder.Callback2 {
|
||||
@Override
|
||||
public void surfaceCreated(final SurfaceHolder holder) {
|
||||
if (mAppContext == 0) {
|
||||
return;
|
||||
}
|
||||
onWindowSurfaceChanged(mAppContext, holder.getSurface());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(
|
||||
final SurfaceHolder holder, final int format, final int width, final int height) {
|
||||
if (mAppContext == 0) {
|
||||
return;
|
||||
}
|
||||
onWindowSurfaceChanged(mAppContext, holder.getSurface());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(final SurfaceHolder holder) {
|
||||
if (mAppContext == 0) {
|
||||
return;
|
||||
}
|
||||
onWindowSurfaceChanged(mAppContext, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceRedrawNeeded(final SurfaceHolder holder) {
|
||||
onWindowSurfaceDraw(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<jp.xenia.emulator.WindowSurfaceView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/window_demo_surface_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="jp.xenia.emulator.WindowDemoActivity">
|
||||
|
||||
</RelativeLayout>
|
||||
tools:context="jp.xenia.emulator.WindowDemoActivity" />
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<resources>
|
||||
<string name="app_name">Xenia</string>
|
||||
<string name="activity_label_window_demo">Xenia Window Demo</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user