9
public class MainActivity extends AppCompatActivity {

private static final String TAG = "MainActivity";
private static final int REQUEST_CODE = 1234;
private int mScreenDensity;
private MediaProjectionManager mProjectionManager;
private static final int DISPLAY_WIDTH = 720;
private static final int DISPLAY_HEIGHT = 1280;
private MediaProjection mMediaProjection;
private VirtualDisplay mVirtualDisplay;
private ToggleButton mToggleButton;
private MediaRecorder mMediaRecorder;
private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
private static final int REQUEST_PERMISSIONS = 10;

static {
    ORIENTATIONS.append(Surface.ROTATION_0, 90);
    ORIENTATIONS.append(Surface.ROTATION_90, 0);
    ORIENTATIONS.append(Surface.ROTATION_180, 270);
    ORIENTATIONS.append(Surface.ROTATION_270, 180);
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);


    DisplayMetrics metrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(metrics);
    mScreenDensity = metrics.densityDpi;


    mProjectionManager = (MediaProjectionManager) getSystemService
            (Context.MEDIA_PROJECTION_SERVICE);

    mToggleButton = (ToggleButton) findViewById(R.id.toggle);
    mToggleButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (ContextCompat.checkSelfPermission(MainActivity.this,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE) + ContextCompat
                    .checkSelfPermission(MainActivity.this,
                            Manifest.permission.RECORD_AUDIO)
                    != PackageManager.PERMISSION_GRANTED) {
                if (ActivityCompat.shouldShowRequestPermissionRationale
                        (MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) ||
                        ActivityCompat.shouldShowRequestPermissionRationale
                                (MainActivity.this, Manifest.permission.RECORD_AUDIO)) {
                    mToggleButton.setChecked(false);
                    Snackbar.make(findViewById(android.R.id.content), R.string.label_permissions,
                            Snackbar.LENGTH_INDEFINITE).setAction("ENABLE",
                            new View.OnClickListener() {
                                @Override
                                public void onClick(View v) {
                                    ActivityCompat.requestPermissions(MainActivity.this,
                                            new String[]{Manifest.permission
                                                    .WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO},
                                            REQUEST_PERMISSIONS);
                                }
                            }).show();
                } else {
                    ActivityCompat.requestPermissions(MainActivity.this,
                            new String[]{Manifest.permission
                                    .WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO},
                            REQUEST_PERMISSIONS);
                }
            } else {
                onToggleScreenShare(v);
            }
        }
    });
}

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

    Log.d(TAG, " requestCode " + requestCode + " resultCode " + requestCode);

    if (REQUEST_CODE == requestCode) {
        if (resultCode == RESULT_OK) {
            mMediaProjection = mProjectionManager.getMediaProjection(resultCode, data);
            startRecording(); // defined below
        } else {
            Log.d(TAG, "Persmission denied");
        }
    }
}

private static final String VIDEO_MIME_TYPE = "video/avc";
private static final int VIDEO_WIDTH = 720;
private static final int VIDEO_HEIGHT = 1280;
// …
private boolean mMuxerStarted = false;
private Surface mInputSurface;
private MediaMuxer mMuxer;
private MediaCodec mVideoEncoder;
private MediaCodec.BufferInfo mVideoBufferInfo;
private int mTrackIndex = -1;

private final Handler mDrainHandler = new Handler(Looper.getMainLooper());
private Runnable mDrainEncoderRunnable = new Runnable() {
    @Override
    public void run() {
        drainEncoder();
    }
};

private void startRecording() {

    DisplayManager dm = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE);
    Display defaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);
    if (defaultDisplay == null) {
        throw new RuntimeException("No display found.");
    }
    prepareVideoEncoder();

    try {
        mMuxer = new MediaMuxer(Environment.getExternalStoragePublicDirectory(Environment
                .DIRECTORY_DOWNLOADS) + "/video.mp4", MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
    } catch (IOException ioe) {
        throw new RuntimeException("MediaMuxer creation failed", ioe);
    }

    // Get the display size and density.
    DisplayMetrics metrics = getResources().getDisplayMetrics();
    int screenWidth = metrics.widthPixels;
    int screenHeight = metrics.heightPixels;
    int screenDensity = metrics.densityDpi;

    // Start the video input.
    mMediaProjection.createVirtualDisplay("Recording Display", screenWidth,
            screenHeight, screenDensity, 0 /* flags */, mInputSurface,
            null /* callback */, null /* handler */);

    // Start the encoders
    drainEncoder();
}

private void prepareVideoEncoder() {

    mVideoBufferInfo = new MediaCodec.BufferInfo();
    MediaFormat format = MediaFormat.createVideoFormat(VIDEO_MIME_TYPE, VIDEO_WIDTH, VIDEO_HEIGHT);
    int frameRate = 15; // 30 fps

    // Set some required properties. The media codec may fail if these aren't defined.
    format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
    //format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 8000);
    format.setInteger(MediaFormat.KEY_BIT_RATE, 6000000); // 6Mbps
    format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
    //format.setInteger(MediaFormat.KEY_CAPTURE_RATE, frameRate);
    // format.setInteger(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, 1000000 / frameRate);
    //format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
    format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10); // 1 seconds between I-frames

    // Create a MediaCodec encoder and configure it. Get a Surface we can use for recording into.
    try {
        mVideoEncoder = MediaCodec.createEncoderByType(VIDEO_MIME_TYPE);
        mVideoEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        mInputSurface = mVideoEncoder.createInputSurface();
        mVideoEncoder.start();
    } catch (IOException e) {
        releaseEncoders();
    }
}

private void releaseEncoders() {


    mDrainHandler.removeCallbacks(mDrainEncoderRunnable);
    if (mMuxer != null) {
        if (mMuxerStarted) {
            mMuxer.stop();
        }
        mMuxer.release();
        mMuxer = null;
        mMuxerStarted = false;
    }
    if (mVideoEncoder != null) {
        mVideoEncoder.stop();
        mVideoEncoder.release();
        mVideoEncoder = null;
    }
    if (mInputSurface != null) {
        mInputSurface.release();
        mInputSurface = null;
    }
    if (mMediaProjection != null) {
        mMediaProjection.stop();
        mMediaProjection = null;
    }
    mVideoBufferInfo = null;
    //mDrainEncoderRunnable = null;
    mTrackIndex = -1;
}

private boolean drainEncoder() {
    mDrainHandler.removeCallbacks(mDrainEncoderRunnable);
    while (true) {
        int bufferIndex = mVideoEncoder.dequeueOutputBuffer(mVideoBufferInfo, 0);

        if (bufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
            // nothing available yet
            break;
        } else if (bufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
            // should happen before receiving buffers, and should only happen once
            if (mTrackIndex >= 0) {
                throw new RuntimeException("format changed twice");
            }
            mTrackIndex = mMuxer.addTrack(mVideoEncoder.getOutputFormat());
            if (!mMuxerStarted && mTrackIndex >= 0) {
                mMuxer.start();
                mMuxerStarted = true;
            }
        } else if (bufferIndex < 0) {
            // not sure what's going on, ignore it
        } else {
            ByteBuffer encodedData = mVideoEncoder.getOutputBuffer(bufferIndex);
            if (encodedData == null) {
                throw new RuntimeException("couldn't fetch buffer at index " + bufferIndex);
            }

            if ((mVideoBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
                mVideoBufferInfo.size = 0;
            }

            if (mVideoBufferInfo.size != 0) {
                if (mMuxerStarted) {
                    encodedData.position(mVideoBufferInfo.offset);
                    encodedData.limit(mVideoBufferInfo.offset + mVideoBufferInfo.size);
                    mMuxer.writeSampleData(mTrackIndex, encodedData, mVideoBufferInfo);
                } else {
                    // muxer not started
                }
            }

            mVideoEncoder.releaseOutputBuffer(bufferIndex, false);

            if ((mVideoBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                break;
            }
        }
        Log.d(TAG, "Recording");
    }

    mDrainHandler.postDelayed(mDrainEncoderRunnable, 10);

    return false;
}


public void onToggleScreenShare(View view) {
    if (((ToggleButton) view).isChecked()) {
        if (mMediaProjection == null) {
            startActivityForResult(mProjectionManager.createScreenCaptureIntent(), REQUEST_CODE);
        } else {
            startRecording();
        }
    } else {
        releaseEncoders();
    }
}

}

startActivityForResult(mProjectionManager.createScreenCaptureIntent(), REQUEST_CODE);

This line of code request permission for capturing screen. Each time my code call that and it show a dialog for permission. But if i click "don't show this again", it won't request permission but grant permission in the background. How can i take permission only once and grant all time without selecting don't show again?? Full code is given here

Md. Sulayman
  • 781
  • 8
  • 30

1 Answers1

1
public void onToggleScreenShare(View view) {
if (((ToggleButton) view).isChecked()) {
    if (mMediaProjection == null) {
        startActivityForResult(mProjectionManager.createScreenCaptureIntent(), REQUEST_CODE);
    } else {
        startRecording();
    }
} else {
    releaseEncoders();
}
}

On this Method startActivityForResult() method prompt screen capturing permission. If grant permission or deny it the code transfer call to onActivityResultMethod()

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

Log.d(TAG, " requestCode " + requestCode + " resultCode " + requestCode);

if (REQUEST_CODE == requestCode) {
    if (resultCode == RESULT_OK) {
        mMediaProjection = mProjectionManager.getMediaProjection(resultCode, data);
        startRecording(); // defined below
    } else {
        Log.d(TAG, "Persmission denied");
    }
}
}

On this method we get Intent data and resultCode. To further use MediaProjectionManager without requesting continuous permission, we have to save reference of the Intent and value of resultCode and use mediaProjectionManager via this line of code

mMediaProjection = mProjectionManager.getMediaProjection(saveResult, savedIntent);

So it won't request permission again as permission is already granted

Md. Sulayman
  • 781
  • 8
  • 30
  • Does it work? How could you save Intent in SharedPreferenses and restore? I try prefs.pusString(key, intent.toUri(0)); and restore Intent intent = Intent.parseUri(uriStr, 0); But when I init mediaprojection with this intent, mediaprojection == null. – Tim Kruichkov May 18 '18 at 09:18
  • Don't save it in SharedPreference. Create a private variable of Intent type on your activity and assign the intent reference on that variable and use it – Md. Sulayman May 18 '18 at 09:27
  • You can try to save the intent on Singleton and try using it on others activity. Though I didn't try this. my need was fulfilled by that approach – Md. Sulayman May 18 '18 at 09:28
  • To save in a private variable will not work in telephone restart. – Rougher Jul 03 '19 at 05:20
  • @TimKruichkov have you found solution to save intent data in `SharedPreferenses ` same issue iam fasing please help – Sagar Jul 30 '19 at 04:12