Home » Android » Android MediaProjectionManager in Service

Android MediaProjectionManager in Service

Posted by: admin June 15, 2020 Leave a comment

Questions:

I want to build an app where I have to use MediaProjectionManager in a Service. But I can not solve it as ‘startActivityForResult’ can’t use in Service class.

How to&Answers:

I really want to do this from a service, which is how I found this question. This is the closest I’ve came up, so just throwing this out there, till a better answer comes along. Here’s a way to do it from an activity that’s almost like doing it from a service:

import static your.package.YourClass.mediaProjectionManager;

public class MainActivity extends Activity {

@Override
protected void onCreate(final Bundle savedInstanceState) {
    super.onCreate(null);
    mediaProjectionManager = (MediaProjectionManager)getContext().getSystemService(MEDIA_PROJECTION_SERVICE);
    startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), 1);
    }

@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();
        }
    }
}

Then in your service when ever you need permission call

private void openMainActivity() {
    Intent mainIntent = new Intent(getContext(), MainActivity.class);
    mainIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(mainIntent);
    }

To make the activity invisible in your AndroidManifest.xml

    <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:theme="@android:style/Theme.NoDisplay"
        android:excludeFromRecents="true"
        android:screenOrientation="portrait">
    </activity>

Caveats:

For a brief second the application you’re screenshooting will lose focus.

For a brief second your app will be the foreground app, so don’t trip over your own shoelaces

Answer:

Ok, I have managed to do this its not the cleanest of ways but works:

  1. Local Bound Service: Make your Service a Local Bound Service, which means your service will pass a reference of itself to the the Main Activity.
  2. Pass an Instance of the Activity to the Service : Pass the reference of your activity to the service in the Service Connection-> onServiceConnected() in the Activity class
  3. Call the startActivityForResult: Use the Activity Instance in your service to Call the startActivityForResult function.

Note:
Upon being called the startActivityForResult will be caught in the onActivityResult() in the Activity Class. You will need to override this function in the Activity and use the service reference to return back to the service class. Also you can unbind the Activity, from the service upon completion.

I hope this makes sense, sorry for not including any code. If you have any questions ill be glad to help you in further detail.

Answer:

I have used same approach as here https://medium.com/@debuggingisfun/android-10-audio-capture-77dd8e9070f9
in my activity:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Intent intent = new Intent(this, MediaProjectionService.class);
    startForegroundService(intent);
    mProjectionManager =
            (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
    startActivityForResult(mProjectionManager.createScreenCaptureIntent(),
            1);
}

Service itself is just some boilerplate, the notification is required if you want to run in foreground

class MediaProjectionService : Service() {

    override fun onCreate() {
        super.onCreate()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val builder = NotificationCompat.Builder(this, "messages")
                    .setContentText("streaming enabled")
                    .setContentTitle("Stream to e-ink")
            startForeground(101, builder.build())
        }
    }

    override fun onBind(intent: Intent): IBinder? {
        return null
    }
}

I am not an android expert and I am using Flutter – apperantly you also need to create a notification channel first – here is my code for Flutter Application – it’s probably similar with non-flutter

class StreaminkApplication : FlutterApplication() {
    override fun onCreate() {
        super.onCreate()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel("messages", "Messages", NotificationManager.IMPORTANCE_LOW)
            val manager = getSystemService(NotificationManager::class.java)
            manager.createNotificationChannel(channel)
        }
    }
}

If you use application, you should change it in manifest – in my case:

<application
    android:name=".StreaminkApplication"