Skip to content

Commit 9e06146

Browse files
committed
Unity 2023 android: Replace UnityPlayer with UnityPlayerForActivityOrService and handle it not inheriting FrameLayout.
1 parent 45fffb5 commit 9e06146

File tree

5 files changed

+106
-72
lines changed

5 files changed

+106
-72
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.xraph.plugin.flutter_unity_widget
2+
3+
import android.annotation.SuppressLint
4+
import android.util.Log
5+
import android.view.InputDevice
6+
import android.view.MotionEvent
7+
import com.xraph.plugin.flutter_unity_widget.UnityPlayerUtils
8+
import android.content.Context
9+
import android.content.res.Configuration
10+
import android.widget.FrameLayout
11+
12+
// These functions used to be in CustomUnityPlayer as UnityPlayer used to extend FrameLayout in Unity < 2023.
13+
// We now use these on FlutterUnityWidgetController's framelayout, which is 1 parent up in the hierarchy.
14+
15+
16+
public class CustomFrameLayout : FrameLayout {
17+
18+
constructor(context: Context) : super(context)
19+
20+
21+
companion object {
22+
internal const val LOG_TAG = "CustomUnityFrameLayout"
23+
}
24+
25+
override fun onConfigurationChanged(newConfig: Configuration?) {
26+
Log.i(LOG_TAG, "ORIENTATION CHANGED")
27+
super.onConfigurationChanged(newConfig)
28+
}
29+
30+
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
31+
// Log.i(LOG_TAG, "dispatchTouch")
32+
event.source = InputDevice.SOURCE_TOUCHSCREEN
33+
34+
// instead of modifying the event in Unity onTouchEvent, intercept it before Unity gets it.
35+
36+
// true for Flutter Virtual Display, false for Hybrid composition.
37+
if (event.deviceId == 0) {
38+
39+
// Flutter creates a touchscreen motion event with deviceId 0. (https://github.com/flutter/flutter/blob/34b454f42dd6f8721dfe43fc7de5d215705b5e52/packages/flutter/lib/src/services/platform_views.dart#L639)
40+
// Unity's new Input System package does not detect these touches, copy the motion event to change the immutable deviceId.
41+
42+
val modifiedEvent = event.copy(deviceId = -1)
43+
event.recycle()
44+
return super.dispatchTouchEvent(modifiedEvent)
45+
} else {
46+
return super.dispatchTouchEvent(event)
47+
}
48+
}
49+
50+
}

android/src/main/kotlin/com/xraph/plugin/flutter_unity_widget/CustomUnityPlayer.kt

Lines changed: 3 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -4,61 +4,15 @@ import android.annotation.SuppressLint
44
import android.app.Activity
55
import android.content.res.Configuration
66
import android.util.Log
7-
import android.view.InputDevice
8-
import android.view.MotionEvent
97
import com.unity3d.player.IUnityPlayerLifecycleEvents
10-
import com.unity3d.player.UnityPlayer
8+
import com.unity3d.player.UnityPlayerForActivityOrService
119

1210
@SuppressLint("NewApi")
13-
class CustomUnityPlayer(context: Activity, upl: IUnityPlayerLifecycleEvents?) : UnityPlayer(context, upl) {
11+
class CustomUnityPlayer(context: Activity, upl: IUnityPlayerLifecycleEvents?) : UnityPlayerForActivityOrService(context, upl) {
1412

1513
companion object {
1614
internal const val LOG_TAG = "CustomUnityPlayer"
1715
}
1816

19-
override fun onConfigurationChanged(newConfig: Configuration?) {
20-
Log.i(LOG_TAG, "ORIENTATION CHANGED")
21-
super.onConfigurationChanged(newConfig)
22-
}
23-
24-
override fun onAttachedToWindow() {
25-
Log.i(LOG_TAG, "onAttachedToWindow")
26-
super.onAttachedToWindow()
27-
UnityPlayerUtils.resume()
28-
UnityPlayerUtils.pause()
29-
UnityPlayerUtils.resume()
30-
}
31-
32-
override fun onDetachedFromWindow() {
33-
Log.i(LOG_TAG, "onDetachedFromWindow")
34-
// todo: fix more than one unity view, don't add to background.
35-
// UnityPlayerUtils.addUnityViewToBackground()
36-
super.onDetachedFromWindow()
37-
}
38-
39-
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
40-
ev.source = InputDevice.SOURCE_TOUCHSCREEN
41-
return super.dispatchTouchEvent(ev)
42-
}
43-
44-
@SuppressLint("ClickableViewAccessibility")
45-
override fun onTouchEvent(event: MotionEvent?): Boolean{
46-
if (event == null) return false
47-
48-
event.source = InputDevice.SOURCE_TOUCHSCREEN
49-
50-
// true for Flutter Virtual Display, false for Hybrid composition.
51-
if (event.deviceId == 0) {
52-
/*
53-
Flutter creates a touchscreen motion event with deviceId 0. (https://github.com/flutter/flutter/blob/34b454f42dd6f8721dfe43fc7de5d215705b5e52/packages/flutter/lib/src/services/platform_views.dart#L639)
54-
Unity's new Input System package does not detect these touches, copy the motion event to change the immutable deviceId.
55-
*/
56-
val modifiedEvent = event.copy(deviceId = -1)
57-
event.recycle()
58-
return super.onTouchEvent(modifiedEvent)
59-
} else {
60-
return super.onTouchEvent(event)
61-
}
62-
}
63-
17+
// former FrameLayout override functions moved to CustomFrameLayout and UnityPlayerUtils (unityAttachListener).
6418
}

android/src/main/kotlin/com/xraph/plugin/flutter_unity_widget/FlutterUnityWidgetController.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class FlutterUnityWidgetController(
4545
private val methodChannel: MethodChannel
4646

4747
private var methodChannelResult: MethodChannel.Result? = null
48-
private var view: FrameLayout
48+
private var view: CustomFrameLayout
4949
private var disposed: Boolean = false
5050
private var attached: Boolean = false
5151
private var loadedCallbackPending: Boolean = false
@@ -56,7 +56,7 @@ class FlutterUnityWidgetController(
5656
var tempContext = UnityPlayerUtils.activity as Context
5757
if (context != null) tempContext = context
5858
// set layout view
59-
view = FrameLayout(tempContext)
59+
view = CustomFrameLayout(tempContext)
6060
view.setBackgroundColor(Color.TRANSPARENT)
6161

6262
// setup method channel
@@ -331,15 +331,15 @@ class FlutterUnityWidgetController(
331331

332332

333333
private fun attachToView() {
334-
if (UnityPlayerUtils.unityPlayer == null) return
334+
if (UnityPlayerUtils.unityFrameLayout == null) return
335335
Log.d(LOG_TAG, "Attaching unity to view")
336336

337-
if (UnityPlayerUtils.unityPlayer!!.parent != null) {
338-
(UnityPlayerUtils.unityPlayer!!.parent as ViewGroup).removeView(UnityPlayerUtils.unityPlayer)
337+
if (UnityPlayerUtils.unityFrameLayout!!.parent != null) {
338+
(UnityPlayerUtils.unityFrameLayout!!.parent as ViewGroup).removeView(UnityPlayerUtils.unityFrameLayout)
339339
}
340340

341341
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
342-
UnityPlayerUtils.unityPlayer!!.z = -1f
342+
UnityPlayerUtils.unityFrameLayout!!.z = -1f
343343
}
344344

345345
// add unity to view
@@ -356,7 +356,7 @@ class FlutterUnityWidgetController(
356356
}
357357

358358
fun reattachToView() {
359-
if (UnityPlayerUtils.unityPlayer!!.parent != view) {
359+
if (UnityPlayerUtils.unityFrameLayout!!.parent != view) {
360360
this.attachToView()
361361
Handler(Looper.getMainLooper()).post {
362362
methodChannel.invokeMethod("events#onViewReattached", null)

android/src/main/kotlin/com/xraph/plugin/flutter_unity_widget/OverrideUnityActivity.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import android.os.Bundle
55
import android.util.Log
66
import android.view.WindowManager
77
import com.unity3d.player.UnityPlayerActivity
8+
import com.unity3d.player.UnityPlayerForActivityOrService
89
import java.util.Objects
910

1011
class OverrideUnityActivity : UnityPlayerActivity() {
@@ -26,7 +27,7 @@ class OverrideUnityActivity : UnityPlayerActivity() {
2627
}
2728

2829
private fun quitPlayer() {
29-
mUnityPlayer?.quit()
30+
mUnityPlayer?.destroy() // unity 2023+ has no quit
3031
}
3132

3233
private fun showMainActivity() {
@@ -42,7 +43,8 @@ class OverrideUnityActivity : UnityPlayerActivity() {
4243

4344
override fun onLowMemory() {
4445
super.onLowMemory()
45-
mUnityPlayer?.lowMemory()
46+
// copied from Unity 2023 UnityPlayerForActivityOrService onLowMemory()
47+
mUnityPlayer.onTrimMemory(UnityPlayerForActivityOrService.MemoryUsage.Critical);
4648
}
4749

4850
override fun onNewIntent(intent: Intent) {

android/src/main/kotlin/com/xraph/plugin/flutter_unity_widget/UnityPlayerUtils.kt

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class UnityPlayerUtils {
2121
private const val LOG_TAG = "UnityPlayerUtils"
2222

2323
var controllers: ArrayList<FlutterUnityWidgetController> = ArrayList()
24+
var unityFrameLayout: FrameLayout? = null
2425
var unityPlayer: CustomUnityPlayer? = null
2526
var activity: Activity? = null
2627
var prevActivityRequestedOrientation: Int? = null
@@ -33,9 +34,23 @@ class UnityPlayerUtils {
3334

3435
private val mUnityEventListeners = CopyOnWriteArraySet<UnityEventListener>()
3536

37+
// In 2023+ we can no longer override the UnityPlayer Framelayout (onAttachedToWindow).
38+
private val unityAttachListener = object : View.OnAttachStateChangeListener {
39+
override fun onViewAttachedToWindow(view: View) {
40+
Log.i(LOG_TAG, "onAttachedToWindow")
41+
UnityPlayerUtils.resume()
42+
UnityPlayerUtils.pause()
43+
UnityPlayerUtils.resume()
44+
}
45+
46+
override fun onViewDetachedFromWindow(view: View) {
47+
Log.i(LOG_TAG, "onDetachedFromWindow")
48+
}
49+
}
50+
3651
fun focus() {
3752
try {
38-
unityPlayer!!.windowFocusChanged(unityPlayer!!.requestFocus())
53+
unityPlayer!!.windowFocusChanged(unityFrameLayout!!.requestFocus())
3954
unityPlayer!!.resume()
4055
} catch (e: Exception) {
4156
Log.e(LOG_TAG, e.toString())
@@ -51,18 +66,19 @@ class UnityPlayerUtils {
5166
throw java.lang.Exception("Unity activity is null")
5267
}
5368

54-
if (unityPlayer != null) {
69+
if (unityFrameLayout != null) {
5570
unityLoaded = true
56-
unityPlayer!!.bringToFront()
57-
unityPlayer!!.requestLayout()
58-
unityPlayer!!.invalidate()
71+
unityFrameLayout!!.bringToFront()
72+
unityFrameLayout!!.requestLayout()
73+
unityFrameLayout!!.invalidate()
5974
focus()
6075
callback?.onReady()
6176
return
6277
}
6378

6479
try {
6580
unityPlayer = CustomUnityPlayer(activity!!, ule)
81+
unityFrameLayout = unityPlayer!!.getFrameLayout()
6682

6783
// Assign mUnityPlayer in the Activity, see FlutterUnityActivity.kt for more details
6884
if(activity is FlutterUnityActivity) {
@@ -78,6 +94,9 @@ class UnityPlayerUtils {
7894
// addUnityViewToBackground(activity!!)
7995
unityLoaded = true
8096

97+
// add onAttachedToWindow events
98+
addUnityAttachListener()
99+
81100
if (!options.fullscreenEnabled) {
82101
activity!!.window.addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
83102
activity!!.window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
@@ -132,7 +151,8 @@ class UnityPlayerUtils {
132151
fun quitPlayer() {
133152
try {
134153
if (unityPlayer != null) {
135-
unityPlayer!!.quit()
154+
removeUnityAttachListener()
155+
unityPlayer!!.destroy()
136156
unityLoaded = false
137157
}
138158
} catch (e: Error) {
@@ -177,6 +197,14 @@ class UnityPlayerUtils {
177197
mUnityEventListeners.remove(listener)
178198
}
179199

200+
private fun addUnityAttachListener() {
201+
unityFrameLayout?.addOnAttachStateChangeListener(unityAttachListener)
202+
}
203+
204+
private fun removeUnityAttachListener() {
205+
unityFrameLayout?.removeOnAttachStateChangeListener(unityAttachListener)
206+
}
207+
180208
private fun shakeActivity() {
181209
unityPlayer?.windowFocusChanged(true)
182210
if (prevActivityRequestedOrientation != null) {
@@ -185,9 +213,9 @@ class UnityPlayerUtils {
185213
}
186214

187215
fun removePlayer(controller: FlutterUnityWidgetController) {
188-
if (unityPlayer!!.parent == controller.view) {
216+
if (unityFrameLayout!!.parent == controller.view) {
189217
if (controllers.isEmpty()) {
190-
(controller.view as FrameLayout).removeView(unityPlayer)
218+
(controller.view as FrameLayout).removeView(unityFrameLayout)
191219
pause()
192220
shakeActivity()
193221
} else {
@@ -204,21 +232,21 @@ class UnityPlayerUtils {
204232
val layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT)
205233
// val layoutParams = ViewGroup.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)
206234
// val layoutParams = ViewGroup.LayoutParams(570, 770)
207-
group.addView(unityPlayer, layoutParams)
235+
group.addView(unityFrameLayout, layoutParams)
208236
}
209237

210238
fun addUnityViewToBackground() {
211-
if (unityPlayer == null) {
239+
if (unityFrameLayout == null) {
212240
return
213241
}
214-
if (unityPlayer!!.parent != null) {
215-
(unityPlayer!!.parent as ViewGroup).removeView(unityPlayer)
242+
if (unityFrameLayout!!.parent != null) {
243+
(unityFrameLayout!!.parent as ViewGroup).removeView(unityFrameLayout)
216244
}
217245
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
218-
unityPlayer!!.z = -1f
246+
unityFrameLayout!!.z = -1f
219247
}
220248
val layoutParams = ViewGroup.LayoutParams(1, 1)
221-
activity!!.addContentView(unityPlayer, layoutParams)
249+
activity!!.addContentView(unityFrameLayout, layoutParams)
222250
}
223251
}
224252
}

0 commit comments

Comments
 (0)