Как получить менеджер медиапроекции, не нарушая текущий процесс переднего плана, за исключением запроса разрешения?

Проблема: у меня есть приложение для скриншотов, которое использует плавающий накладной сервис для элементов управления и экранный API-интерфейс Media Media Manager для доступа к экрану. Иногда, когда устройство мало на памяти, Android перезагружает службу, и я теряю медиапроекцию.

Единственный способ узнать новую медиапроекцию - снова открыть Activity, запрашивающий разрешения, и это будет конец, за исключением одной проблемы. Некоторые приложения, в частности игры, похоже, слушают, когда они теряют процесс переднего плана, пауза или иначе reset. Это раздражает.

Вот мой идеальный сценарий. Служба открывается, если пользователь выбрал "проверять не спрашивать меня снова" в диалоговом окне запроса на запрос, он получает медиапроекцию таким образом, чтобы не нарушать текущую активность переднего плана.

Как получить менеджер медиапроекции без нарушения текущего процесса переднего плана?

Есть ли способ получить медиапроекцию из прямой службы или способ открыть активность в фоновом режиме из службы?

В настоящее время я использую этот код в Activity, чтобы получить менеджер медиапроекции

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
protected void getScreenShotPermission() {
    if(isLollipopOrNewer) {
        mediaProjectionManager = (MediaProjectionManager) getContext().getSystemService(MEDIA_PROJECTION_SERVICE);
        startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), 1);
    }
}




@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == 1) {
        if (resultCode == Activity.RESULT_OK) {
            mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data);
            this.finish();
        }
    }

}

Ответ 1

Итак, я вернулся к этому, потому что это было глупо, и это меня подцепило, и я понял это!

В другом классе (в моем классе приложения) введите этот код:

private static Intent screenshotPermission = null;

protected static void getScreenshotPermission() {
    try {
        if (hasScreenshotPermission()) {
            if(null != mediaProjection) {
                mediaProjection.stop();
                mediaProjection = null;
            }
            mediaProjection = mediaProjectionManager.getMediaProjection(Activity.RESULT_OK, (Intent) screenshotPermission.clone()); 
        } else {
            openScreenshotPermissionRequester();
        }
    } catch (final RuntimeException ignored) {
        openScreenshotPermissionRequester();
    }
}

protected static void openScreenshotPermissionRequester(){
    final Intent intent = new Intent(context, AcquireScreenshotPermissionIntent.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(intent);
}



protected static void setScreenshotPermission(final Intent permissionIntent) {
    screenshotPermission = permissionIntent;
}

В вашем классе активности, обрабатывающем первоначальный запрос (в моем случае: AcquireScreenshotPermissionIntent), введите этот код в свой onactivityresult:

@Override
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (1 == requestCode) {
        if (Activity.RESULT_OK == resultCode) {
            setScreenshotPermission((Intent) data.clone());
        }
    } else if (Activity.RESULT_CANCELED == resultCode) {
        setScreenshotPermission(null);
        log("no access");

    }
    finish();

Просто позвоните getScreenShotPermission(), когда вам нужно разрешение, затем используйте результирующий объект mediaProjection.

Вот как это работает: волшебный токен - это некоторые данные, включенные в намерение. Первоначально я пытался поставить результат на глобальную переменную и использовать ее для создания медиапроекции из класса nonactivity. Проблема в том, что это провалится. В конечном итоге я понял, что токен потребляется при создании медиапроекции. Передача его в качестве аргумента или присваивание новой переменной просто передает указатель на нее, и она по-прежнему потребляется.

Вместо этого вам нужно использовать object.clone();. Это делает новую копию токена, новая копия потребляется, и вы можете создавать дополнительные токены по мере необходимости, если вы не используете оригинал. В качестве бонуса вашему приложению нужно только спросить разрешение на скриншот один раз за запуск. Если что-то еще берет на себя скрипт, или менеджер памяти Android получает вас, вы закрыты. Вы можете создать новый виртуальный экран без отправки onPause или onStop событий в другие приложения.

Ответ 2

У меня есть служба продолжения, поэтому я объявила одну статическую переменную внутри службы и использую ее, когда пользователь нажимает мою плавающую кнопку (например, chatheads).

public static  Intent data = null;  //declaration 

тогда, когда первый пользователь открывает приложение, я прошу разрешения и устанавливаю значение данных следующим образом.

     @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {

 if(requestCode == 4){
               Log.d(TAG, "onActivityResult: for Screen Capture");
               if(resultCode == RESULT_OK)
               {
                   Log.d(TAG, "onActivityResult: Permission Granted");
                WindowChangeDetectingService.data = (Intent) data.clone();
                screenCapturePermission = true;
                   updateUI();
               }else
               {
                   Log.d(TAG, "onActivityResult: Permission Deined");
                   screenCapturePermission = false;
                   updateUI();
                   SnackBar screenCapturePermissionSnackbar = new SnackBar(this, "This Permission is needed.", "Grant", new View.OnClickListener() {
                       @Override
                       public void onClick(View v) {
                           takeScreenCapturePermission();
                       }
                   });
                   screenCapturePermissionSnackbar.setDismissTimer(2000);
                   screenCapturePermissionSnackbar.show();

               }
           }
    }

Наконец, когда пользователь нажимает на плавающую кнопку Overlay, сначала проверяйте значение данных.

 if(WindowChangeDetectingService.data == null)
        {
            Log.d(TAG, "basicSetup: Asking permission again");
            //open app again
        }else
        {
            Log.d(TAG, "basicSetup: Getting permission for the object");
            readPermissionObject();
        }

Реализация readPermissionObject выглядит следующим образом

 private void readPermissionObject()
    {
       Intent data =  WindowChangeDetectingService.getData();
        mMediaProjectionManager  = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
        startProjection(Activity.RESULT_OK,data);
    }

Благодаря @netsplit для предоставления информации о data.clone();.