Android PictureInPicture

홍승범·2023년 1월 19일
0

Android

목록 보기
2/9

PictureInPicture

API 26부터 지원된 기능으로 액티비티를 pip 모드로 실행할 수 있다.

해당 기능을 사용하기 위해서는 manifest 파일의 액티비티 부분에 android:supportPictureIPicture 속성을 true로 주면 사용할 수 있다.

주요 사용법 및 특징은 다음과 같다

  • 액티비티에서 동작중에 pip로 진입하기 위해서는 enterPictureInPicture 메소드를 호출
  • 모드 변경에 대한 콜백은 onPictureInPictureModeChanged 메소드를 오버라이드하여 아래와 같이 받을 수 있다.
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean,
                                newConfig: Configuration) {
    if (isInPictureInPictureMode) {
        // Hide the full-screen UI (controls, etc.) while in picture-in-picture mode.
    } else {
        // Restore the full-screen UI.
    }
}
  • 미디어 재생시 활성화된 미디어 세션이 존재하는경우 컨트롤을 표시해 준다

동작

  1. pip 전환시 라이프사이클 콜백

    • 진입시에는 액티비티가 일시중지되고, onPause 콜백이 호출됨
    • 전체화면으로 전환시에 onResume이 호출됨
    • 동영상 재생의 경우에 잘 생각해 봐야 할 것이, onPause/onResume을 기준으로 MediaPlayer 의 재생을 컨트롤 하는 경우가 문제가 된다. 즉 pip 진입할때도, 화면이 가려질때도, onPause가 불릴 수 있는데, 이러한 경우에 대한 예외처리가 필요하다
      - 재생 처리를 onStop/onStart로 옮기거나, isInPictureInPictureMode 메소드를 통해 현재 상태를 확인하고, 처리를 해주어야 할 것이다.
  2. 태스크 관련 동작.

    • pip 모드로 진입하면, 액티비티는 현재 태스크에서 빠져 따로 동작한다.
    • 앱의 구조가 다음과 같다고 가정하자.
      - MainActivity -> 비디오 정보를 보여주기 위한 DetailActivity -> 재생을 선택하여 PlayerActivity가 뜨는 구조.
    1. 첫번째 시나리오는 재생중 pip진입 -> home 전환 -> 다른 앱에서 멀티태스킹 -> pip 확장할때ex1
      • pip 진입하면서 내 앱의 태스크에서 빠졌다가, 전체화면 전환시 멀티태스킹중이던 앱의 태스크로 붙어버린다.
      • 이상태에서 PlayerActivity가 종료되면 멀티태스킹중이던 앱의 화면이 나타난다.
    2. 두번째 시나리오 재생중 pip 진입 -> 내앱에서 다른일을 하다가 확장ex2
      • 확장하면 어쨌든 따로 존재하나, 그 아래의 태스크가 내 앱이된다.
      • 이경우는 그래도 내 앱이 나타나기 때문에 문제는 없다.
      • 다만 이경우에도 recent screen을 보면 내 앱이 두개가 나오는 불상사(!)가 생긴다(manifest에서 recent에서 뺀다고 선언해주면 되긴하지만)
    3. 여기서 하나더 생각해 볼 시나리오, 만약 2번 시나리오에서 pip상태에서 다시 재생을 누른다면?
      • launchMode가 standard나 singleTop이면 새로운 PlayerActivity가 뜬다.
      • 내 태스크 백스택의 탑은 PlayerActivity가 아니기 때문이다. 따라서 pip상태의 액티비티에서 onNewIntent도 받을 수가 없다!
      • 위와같은 이유로 인해 pip동작중 onNewIntent를 통한 새로운 비디오 재생등을 처리하려면, PlayerActivity의 launchMode는 singleTask가 되어야 한다. 이점은 구글 디벨로퍼 가이드에도 명시되어있다.
      • singleTask로 설정해도 문제가 남아있는 것이, pip 모드로 멀티태스킹을 하다가도, 확장하고 액티비티를 종료하면, 내 앱의 화면이 뜨는걸 기대하는 것이 맞을것이다. 이를 위해 다음과 같은 해결 방법들이 있다

해결방법에대한 고민

크게 2가지 방법이 있다

  1. pip 진입시 해당 액티비티에서는 더 이상 백스택을 사용하지 못하기 때문에, 강제로 원래 태스크를 가져오는 방법이다.
  • 태스크를 종료하고 새로 태스크를 생성해 루트 액티비티(Main)을 띄우는 방법
@Override
public void finish () {
    if( mBackstackLost ){
        finishAndRemoveTask();
        startActivity(
            Intent.makeRestartActivityTask(
                new ComponentName(this, MockHomeActivity.class)));
    } else {
        super.finish();
    }
}
  • 또는 런처의 태스크를 포어그라운드로 불러오는 방법
/**
* Bring up launcher task to front
*/
public static void navToLauncherTask(Context appContext) {
    ActivityManager activityManager = (ActivityManager) appContext.getSystemService(Context.ACTIVITY_SERVICE);
    // iterate app tasks available and navigate to launcher task (browse task)
    assert activityManager != null;
    final List<ActivityManager.AppTask> appTasks = activityManager.getAppTasks();
    for (ActivityManager.AppTask task : appTasks) {
        final Intent baseIntent = task.getTaskInfo().baseIntent;
        final Set<String> categories = baseIntent.getCategories();
        if (categories != null && categories.contains(Intent.CATEGORY_LAUNCHER)) {
            task.moveToFront();
            return;
        }
    }
}
  1. 두번째 방법은 앱을 하나의 액티비티로에 각 화면을 Fragment로 구성하는 방법이 있다.
  • 프래그먼트는 액티비티 백스택과는 다르게 액티비티내의 백스택에서 관리되므로 pip 변경을 해도 지장이 없다.
    ex3

사실 2번이 제일 깔끔하게 처리할 수 있는 것으로 보인다.


https://medium.com/androiddevelopers/navigation-patterns-with-pip-2c5b6a446ba0

https://medium.com/mindorks/android-picture-in-picture-mode-and-backstack-management-for-multiple-activities-8427fe3f8102

https://developer.android.com/guide/topics/ui/picture-in-picture?hl=ko

profile
그냥 사람

0개의 댓글