Assets/Plugin/Android/Activity.java설정
- 아래 코드는 Android의 홈메뉴를 눌렀을 시, Pip모드로 전환하는 코드이다.
public class Activity extends UnityPlayerActivity
{
boolean usePiPMode = false; //pip를 사용하는 경우에 대한 플래그
boolean isConfigurationInPip = false; //pip창에서 닫기 이벤트 인지를 확인하기 위한 플래그
@Override
protected void onUserLeaveHint()
{
//home메뉴 터치 시, 동작.
super.onUserLeaveHint();
if(usePiPMode)
{
enterPipMode();
}
}
@Override
protected void onPause()
{
super.onPause();
if(IsInPipMode())
{
//onUserLeaveHint -> onPause 호출됨
//UnityPlayer가 onPause시 내부에서 pause된다. 이것을 다시 resume으로 바꿔 활성화 시켜줘야한다.
mUnityPlayer.resume();
}
}
//pip전환 함수
private void enterPipMode()
{
if (!IsSupportedPIP())
{
return;
}
if (!IsAvailablePIP())
{
return;
}
if (mUnityPlayer == null)
{
return;
}
int width = mUnityPlayer.getWidth();
int height = mUnityPlayer.getHeight();
float ratio = width/(float)height;
if(ratio > 2.39f)
{
//비율이 2.39이상인 경우 에러가 발생한다. 때문에 강제로 2.39f아래로 맞춰준다.
width = (int)(height*2.38f);
}
Rational rational = new Rational(width, height);
PictureInPictureParams params = new PictureInPictureParams.Builder()
.setAspectRatio(rational)
.build();
//pip화면으로 전환. 명시적 전환
enterPictureInPictureMode(params);
// target 32부터 사용가능한 함수
// 자동전환
// setPictureInPictureParams 호출 시, pause로 넘어갈 시 자동으로 pip로 전환됨
// 이 경우엔, onCreate나 초기화 해주는 함수에서 사용하는게 좋다.
//PictureInPictureParams params = new PictureInPictureParams.Builder()
// .setAspectRatio(rational)
// .setAutoEnterEnabled(usePiPMode) //false이면 전환 안함.
// .build();
//setPictureInPictureParams(params);
}
private boolean IsSupportedPIP()
{
// Android 8.0 (API level 26)이상부터 지원한다.
return Build.VERSION.SDK_INT >= 26;
}
private boolean IsAvailablePIP()
{
// 메모리 사용가능한지 확인. Ram부족 시 사용 안됨.
boolean supported = getPackageManager().hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE);
return supported;
}
private void UsePiPMode(boolean _usePiPMode)
{
//Unity에서 호출하는 함수. pip모드를 사용할지 결정하는 함수이다.
usePiPMode = _usePiPMode;
}
private boolean IsInPipMode()
{
if(!IsSupportedPIP() || !IsAvailablePIP())
{
return false;
}
return isInPictureInPictureMode();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
//Configuration 차이 확인
int diff = 0;
if(oldConfig != null)
{
diff = oldConfig.diff(newConfig);
}
oldConfig = new Configuration(newConfig);
if(IsInPipMode())
{
//pip모드에서 x버튼(닫기버튼)을 눌렀을 경우 처리를 위한 코드
//UnityPlayer의 surfaceView가 날아가기 때문에, 그 전에 pause로 바꿔서 렌더링을 안하게 해야한다.
//unity세팅에서 multithread redering을 비활성화 하면, 안해줘도 된다.
if(isConfigurationInPip)
{
//x버튼을 눌러 pip창을 닫은 경우
//UnityPlayer를 pause로 만들어줘야한다. 안하면 에러 발생
//onPictureInPictureModeChanged 가 나중에 호출된다.
//때문에 onPictureInPictureModeChanged 대신, onConfigurationChanged를 활용하게됨.
//이떄 유저가 pip창 사이즈 조절하는 행동은 무시하기 위해, diff값에 CONFIG_SCREEN_LAYOUT가 포함되었는지 확인해야한다.
if((diff & ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0 &&
(diff & ActivityInfo.CONFIG_ORIENTATION) != 0)
{
isConfigurationInPip = false;
mUnityPlayer.pause();
}
}
else
{
//pip모드로 들어온 경우.
//onPictureInPictureModeChanged 가 먼저 호출되었기 때문에, IsInPipMode가 true이다.
isConfigurationInPip = true;
}
}
else
{
//원래 화면으로 복구되는 경우.
// Restore the full-screen UI.
isConfigurationInPip = false;
}
}
@Override
public void onPictureInPictureModeChanged (boolean isInPictureInPictureMode, Configuration newConfig) {
if (isInPictureInPictureMode) {
// Hide the full-screen UI (controls, etc.) while in picture-in-picture mode.
// pip모드로 전환된 경우 처리
} else {
// Restore the full-screen UI.
// pip모드에서 원래 창으로 돌아가는 경우 처리.
// 닫기 버튼 클릭시에는 호출 안됨.
}
}
}
위 코드에서, Pip창에서 x버튼을 눌러 닫는 경우에 대한 처리를 onConfigurationChanged통해 하고 있다. x버튼을 누르는 경우
onConfigurationChanged → surfaceDestroyed → onStop → onPictureInPictureModeChanged → 순으로 호출된다.
surfaceDestroyed 가 되기 전에, mUnityPlayer를 pause로 해줘야 한다. 아니면 에러가 발생함. (surfaceview가 destroy됐는데, 렌더링을 시도해서 발생함)
Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x2d8 in tid 15182 (UnityGfxDeviceW), pid 15065
pip모드로 들어올 때에는 onPictureInPictureModeChanged → onConfigurationChanged 으로 호출된다.
원래 화면으로 복구 하는 경우에도 onPictureInPictureModeChanged → onConfigurationChanged 으로 호출된다.
isConfigurationInPip 변수를 두어, pip모드에서 닫기를 한 것인지 체크할 수 있도록 로직을 설계했다.
- 최신 Unity 버전에서는 해당 에러가 발생하지 않아서, 저렇게 처리 하지 않아도 된다