maranR
maranR

Reputation: 423

How can I prevent the app from going back to the home screen when initializing the MediaProjection API?

I have created a custom launcher screen application that is displayed within the device applications. Within the application, I have included a screen recording feature using the media projection API. Everything was working perfectly, but whenever I initiate the screen recording, it goes back to the home screen. I want the screen recording to overlay the currently opened application instead of going back to the home screen. Can anyone help me solve this issue?

here is my code:-

 //ScreenRecordingService.java
package com.Services;

import static android.app.Activity.RESULT_OK;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import android.view.Surface;

import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;

import com.mdmtesting.hiveliveviewer.R;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScreenRecordingService extends Service {
    private static final String VIDEO_MIME_TYPE = "video/avc";
    private static final int VIDEO_WIDTH = 1280;
    private static final int VIDEO_HEIGHT = 720;
    private static final int VIDEO_BIT_RATE = 5000000;
    private static final int VIDEO_FRAME_RATE = 30;
    private static final int VIDEO_I_FRAME_INTERVAL = 1;
    private static final String TAG = "ScreenRecordingService";
    private static final String CHANNEL_ID = "screen_recording_channel";

    private MediaProjectionManager mediaProjectionManager;
    private MediaProjection mediaProjection;
    private MediaCodec mEncoder;
    private Surface mInputSurface;
    private MediaCodec.BufferInfo mBufferInfo;
    private ScheduledExecutorService scheduler;
    private WebSocketService webSocketService;
    private boolean isBound = false;

    @Override
    public void onCreate() {
        super.onCreate();
        // Create notification channel for Android O and above
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                    CHANNEL_ID,
                    "Screen Recording Service",
                    NotificationManager.IMPORTANCE_LOW
            );
            NotificationManager manager = getSystemService(NotificationManager.class);
            if (manager != null) {
                manager.createNotificationChannel(channel);
            }
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "Service started");

        // Start the service as a foreground service
        startForegroundService();

        Intent webSocketIntent = new Intent(this, WebSocketService.class);
        bindService(webSocketIntent, connection, Context.BIND_AUTO_CREATE);

        if (intent != null && intent.hasExtra("resultData")) {
            Intent resultData = intent.getParcelableExtra("resultData");
            if (resultData != null) {
                startScreenRecording(resultData);
            } else {
                Log.e(TAG, "No resultData in Intent");
                stopSelf();
            }
        } else {
            Log.e(TAG, "Intent does not contain resultData");
            stopSelf();
        }

        return START_STICKY;
    }

    private void startScreenRecording(Intent resultData) {
        mediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
        mediaProjection = mediaProjectionManager.getMediaProjection(RESULT_OK, resultData);

        if (mediaProjection == null) {
            Log.e(TAG, "MediaProjection is null");
            stopSelf();
            return;
        }

        try {
            setupMediaCodec();
            createVirtualDisplay();
        } catch (IOException e) {
            Log.e(TAG, "Error setting up MediaCodec", e);
            stopSelf();
        }
    }

    private void setupMediaCodec() throws IOException {
        mBufferInfo = new MediaCodec.BufferInfo();
        mEncoder = MediaCodec.createEncoderByType(VIDEO_MIME_TYPE);
        MediaFormat format = MediaFormat.createVideoFormat(VIDEO_MIME_TYPE, VIDEO_WIDTH, VIDEO_HEIGHT);
        format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
        format.setInteger(MediaFormat.KEY_BIT_RATE, VIDEO_BIT_RATE);
        format.setInteger(MediaFormat.KEY_FRAME_RATE, VIDEO_FRAME_RATE);
        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, VIDEO_I_FRAME_INTERVAL);
        mEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        mInputSurface = mEncoder.createInputSurface();
        mEncoder.start();
    }

    private void createVirtualDisplay() {
        mediaProjection.createVirtualDisplay(
                "ScreenRecordingService",
                VIDEO_WIDTH, VIDEO_HEIGHT, getResources().getDisplayMetrics().densityDpi,
                android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                mInputSurface, null, null);

        scheduler = Executors.newScheduledThreadPool(1);
        scheduler.scheduleWithFixedDelay(this::encodeAndSendData, 0, 1, TimeUnit.SECONDS); // Adjusted to 1 second
    }

    private void encodeAndSendData() {
        int outputBufferIndex = mEncoder.dequeueOutputBuffer(mBufferInfo, 10000); // Adjusted timeout to 10 seconds
        while (outputBufferIndex >= 0) {
            ByteBuffer outputBuffer = mEncoder.getOutputBuffer(outputBufferIndex);
            if (outputBuffer != null) {
                byte[] outData = new byte[mBufferInfo.size];
                outputBuffer.get(outData);
                outputBuffer.clear();
                if (isH264(outData) && isBound) {
                   //Log.d(TAG, "encodeAndSendData: WebSocketService " + outData.length + " bytes");
                    webSocketService.sendData(outData);
                }
                mEncoder.releaseOutputBuffer(outputBufferIndex, false);
            }
            outputBufferIndex = mEncoder.dequeueOutputBuffer(mBufferInfo, 10000);
        }
    }

    private boolean isH264(byte[] data) {
        if (data.length < 4) return false;
        return (data[0] == 0 && data[1] == 0 && (data[2] == 1 || (data[2] == 0 && data[3] == 1)));
    }

    private void startForegroundService() {
        // Create a notification
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle("Screen Recording")
                .setContentText("Recording in progress...")
                .setSmallIcon(R.mipmap.ic_launcher) // Use a proper icon for your notification
                .setPriority(NotificationCompat.PRIORITY_LOW);

        // Start the service in the foreground
        startForeground(1, builder.build());
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "Service destroyed");
        if (mediaProjection != null) {
            Log.d(TAG, "Stopping MediaProjection");
            mediaProjection.stop();
        }
        if (mEncoder != null) {
            Log.d(TAG, "Stopping MediaCodec");
            mEncoder.stop();
            mEncoder.release();
        }
        if (scheduler != null) {
            Log.d(TAG, "Shutting down scheduler");
            scheduler.shutdown();
        }
        if (isBound) {
            unbindService(connection);
            isBound = false;
        }

        super.onDestroy();
    }

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            WebSocketService.LocalBinder binder = (WebSocketService.LocalBinder) service;
            webSocketService = binder.getService();
            isBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBound = false;
        }
    };
}

Upvotes: 1

Views: 32

Answers (0)

Related Questions