[UI] Android surface [skip appveyor]

This commit is contained in:
Triang3l
2022-02-01 22:18:04 +03:00
parent c6fc8f706a
commit 413d7ded49
14 changed files with 369 additions and 30 deletions

View File

@@ -81,4 +81,8 @@ android {
path file('../../../build/xenia.wks.Android.mk')
}
}
}
dependencies {
implementation 'org.jetbrains:annotations:15.0'
}

View File

@@ -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" />

View File

@@ -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);
}
}

View File

@@ -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));
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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" />

View File

@@ -1,3 +1,4 @@
<resources>
<string name="app_name">Xenia</string>
<string name="activity_label_window_demo">Xenia Window Demo</string>
</resources>