Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import android.app.Activity;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.ViewGroup;
import android.view.WindowManager;

Expand All @@ -11,6 +14,8 @@
import java.lang.reflect.InvocationTargetException;

public class ReactNativeUnity {
private static final String TAG = "ReactNativeUnity";

private static UPlayer unityPlayer;
public static boolean _isUnityReady;
public static boolean _isUnityPaused;
Expand Down Expand Up @@ -51,36 +56,51 @@ public void run() {

try {
unityPlayer = new UPlayer(activity, callback);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | InvocationTargetException e) {}

try {
// wait a moment. fix unity cannot start when startup.
Thread.sleep(1000);
} catch (Exception e) {}

// start unity
try {
addUnityViewToBackground();
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {}

unityPlayer.windowFocusChanged(true);

try {
unityPlayer.requestFocusPlayer();
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {}

unityPlayer.resume();

if (!fullScreen) {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
Log.e(TAG, "Failed to create Unity player", e);
}

_isUnityReady = true;
if (unityPlayer == null) {
Log.e(TAG, "Unity player was not created — aborting initialization");
return;
}

try {
callback.onReady();
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {}
// Defer post-init work to avoid blocking the UI thread.
// The old Thread.sleep(1000) caused ANR risk and rendering issues.
final boolean wasFullScreen = fullScreen;
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
try {
addUnityViewToBackground();
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
Log.e(TAG, "Failed to add Unity view to background", e);
}

unityPlayer.windowFocusChanged(true);

try {
unityPlayer.requestFocusPlayer();
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
Log.w(TAG, "Failed to request focus on Unity player", e);
}

unityPlayer.resume();

if (!wasFullScreen) {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}

_isUnityReady = true;

try {
callback.onReady();
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
Log.e(TAG, "Callback onReady failed", e);
}
}
}, 1000);
}
});
}
Expand Down Expand Up @@ -112,15 +132,21 @@ public static void addUnityViewToBackground() throws InvocationTargetException,
return;
}

android.widget.FrameLayout frame = unityPlayer.requestFrame();
if (frame == null) {
Log.w(TAG, "requestFrame() returned null — cannot add Unity view to background");
return;
}

if (unityPlayer.getParentPlayer() != null) {
// NOTE: If we're being detached as part of the transition, make sure
// to explicitly finish the transition first, as it might still keep
// the view's parent around despite calling `removeView()` here. This
// prevents a crash on an `addContentView()` later on.
// Otherwise, if there's no transition, it's a no-op.
// See https://stackoverflow.com/a/58247331
((ViewGroup) unityPlayer.getParentPlayer()).endViewTransition(unityPlayer.requestFrame());
((ViewGroup) unityPlayer.getParentPlayer()).removeView(unityPlayer.requestFrame());
((ViewGroup) unityPlayer.getParentPlayer()).endViewTransition(frame);
((ViewGroup) unityPlayer.getParentPlayer()).removeView(frame);
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Expand All @@ -129,20 +155,26 @@ public static void addUnityViewToBackground() throws InvocationTargetException,

final Activity activity = ((Activity) unityPlayer.getContextPlayer());
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(1, 1);
activity.addContentView(unityPlayer.requestFrame(), layoutParams);
activity.addContentView(frame, layoutParams);
}

public static void addUnityViewToGroup(ViewGroup group) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
if (unityPlayer == null) {
return;
}

android.widget.FrameLayout frame = unityPlayer.requestFrame();
if (frame == null) {
Log.w(TAG, "requestFrame() returned null — cannot add Unity view to group");
return;
}

if (unityPlayer.getParentPlayer() != null) {
((ViewGroup) unityPlayer.getParentPlayer()).removeView(unityPlayer.requestFrame());
((ViewGroup) unityPlayer.getParentPlayer()).removeView(frame);
}

ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT);
group.addView(unityPlayer.requestFrame(), 0, layoutParams);
group.addView(frame, 0, layoutParams);
unityPlayer.windowFocusChanged(true);
unityPlayer.requestFocusPlayer();
unityPlayer.resume();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@

import android.annotation.SuppressLint;
import android.content.res.Configuration;
import android.util.Log;
import android.widget.FrameLayout;

import java.lang.reflect.InvocationTargetException;

@SuppressLint("ViewConstructor")
public class ReactNativeUnityView extends FrameLayout {
private static final String TAG = "ReactNativeUnityView";
private UPlayer view;
public boolean keepPlayerMounted = false;

Expand Down Expand Up @@ -62,7 +64,7 @@ protected void onDetachedFromWindow() {
try {
addUnityViewToBackground();
} catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
throw new RuntimeException(e);
Log.e(TAG, "Failed to move Unity view to background on detach", e);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import static com.azesmwayreactnativeunity.ReactNativeUnity.*;

import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.View;

import androidx.annotation.NonNull;
Expand All @@ -26,6 +28,7 @@

@ReactModule(name = ReactNativeUnityViewManager.NAME)
public class ReactNativeUnityViewManager extends ReactNativeUnityViewManagerSpec<ReactNativeUnityView> implements LifecycleEventListener, View.OnAttachStateChangeListener {
private static final String TAG = "RNUnityViewManager";
ReactApplicationContext context;
static ReactNativeUnityView view;
public static final String NAME = "RNUnityView";
Expand All @@ -51,7 +54,9 @@ public ReactNativeUnityView createViewInstance(@NonNull ThemedReactContext conte
if (getPlayer() != null) {
try {
view.setUnityPlayer(getPlayer());
} catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {}
} catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
Log.e(TAG, "Failed to set existing Unity player on view", e);
}
} else {
try {
createPlayer(context.getCurrentActivity(), new UnityPlayerCallback() {
Expand All @@ -76,7 +81,9 @@ public void onQuit() {
reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(view.getId(), "onPlayerQuit", data);
}
});
} catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {}
} catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
Log.e(TAG, "Failed to create Unity player", e);
}
}

return view;
Expand Down Expand Up @@ -140,7 +147,11 @@ public void unloadUnity(ReactNativeUnityView view) {
public void pauseUnity(ReactNativeUnityView view, boolean pause) {
if (isUnityReady()) {
assert getPlayer() != null;
getPlayer().pause();
if (pause) {
getPlayer().pause();
} else {
getPlayer().resume();
}
}
}

Expand Down Expand Up @@ -201,7 +212,7 @@ public void onHostDestroy() {
private void restoreUnityUserState() {
// restore the unity player state
if (isUnityPaused()) {
Handler handler = new Handler();
Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
@Override
public void run() {
Expand Down
18 changes: 12 additions & 6 deletions android/src/main/java/com/azesmwayreactnativeunity/UPlayer.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import android.app.Activity;
import android.content.res.Configuration;
import android.util.Log;
import android.widget.FrameLayout;

import com.unity3d.player.*;
Expand All @@ -11,6 +12,7 @@
import java.lang.reflect.Method;

public class UPlayer {
private static final String TAG = "UPlayer";
private static UnityPlayer unityPlayer;

public UPlayer(final Activity activity, final ReactNativeUnity.UnityPlayerCallback callback) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException {
Expand Down Expand Up @@ -91,7 +93,7 @@ public void requestFocusPlayer() throws NoSuchMethodException, InvocationTargetE
}
}

public FrameLayout requestFrame() throws NoSuchMethodException {
public FrameLayout requestFrame() {
try {
Method getFrameLayout = unityPlayer.getClass().getMethod("getFrameLayout");

Expand All @@ -101,17 +103,21 @@ public FrameLayout requestFrame() throws NoSuchMethodException {
if (FrameLayout.class.isInstance(unityPlayer)) {
return FrameLayout.class.cast(unityPlayer);
} else {
Log.w(TAG, "UnityPlayer is not a FrameLayout and has no getFrameLayout() — requestFrame() returning null");
return null;
}
}
}

public void setZ(float v) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
public void setZ(float v) {
try {
Method setZ = unityPlayer.getClass().getMethod("setZ");

setZ.invoke(unityPlayer, v);
} catch (NoSuchMethodException e) {}
FrameLayout frame = this.requestFrame();
if (frame != null) {
frame.setZ(v);
}
} catch (Exception e) {
Log.w(TAG, "Failed to set Z on Unity player", e);
}
}

public Object getContextPlayer() {
Expand Down
2 changes: 1 addition & 1 deletion example/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@

<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="AIzaSyD_XHpYQ8QGYUYnVldanNhhVreAaxk3S9s" />
android:value="YOUR_GOOGLE_MAPS_API_KEY" />

<meta-data
android:name="com.google.ar.core"
Expand Down