Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
356 views
in Technique[技术] by (71.8m points)

android - How do I get MediaProjectionManager without disturbing the current foreground process, except to ask for permission?

Problem: I have a screenshot app that uses a floating overlay service for controls, and screen cast API Media Project Manager to get access to the screen. Sometimes when a device is low on memory Android restarts the service, and I lose my media projection.

The only way I know of to reacquire a new media projection is to re-open an Activity that requests the permissions, and that would be the end of it, except for one problem. Certain apps, particularly games, seem to listen for when they lose the foreground process, and pause, or otherwise reset. This is annoying.

Here's my ideal scenerio. Service opens, if the user has selected " checked don't ask me again" in permission request dialog, it gets the media projection in a way that does not disturb the current foreground activity.

How do I get a media projection manager without disturbing the current foreground process?

Is there either a way to get media projection from a straight service, or a way to open activity in the background from a service?

Currently I use this code in Activity to get the MediaProjectionManager

@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();
        }
    }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

So I came back to this because it was dumb and it was bugging me, and I figured it out!

In another class (in mine it's the application class) put this code:

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;
}

In your activity class handling the initial request (in my case: AcquireScreenshotPermissionIntent) put this code in your 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();

Simply call getScreenShotPermission() whenever you need permission, then use the resulting mediaProjection object.

Here's how it works: The magic token is some data included in the Intent. What I tried initially was putting the result intent a global variable and using it to create the media projection from a nonactivity class. Problem is it would fail. What I eventually figured out is the token gets consumed when you create a media projection with it. Passing it as an argument or assigning to a new variable just passes a pointer to it, and it still gets consumed.

What you need to do instead is use object.clone();. This makes a new copy of the token, the new copy gets consumed, and you can create additional tokens as needed, as long as you don't consume the original. As a bonus your app only has to ask for screenshot permission once per launch. If something else takes over the screencast, or the Android memory manager gets you, you're covered. You can create a new virtual screen without sending onPause or onStop events to other apps.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...