Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Please add request permission first then record later feature. #123

Open
night-angel-star opened this issue Dec 23, 2024 · 0 comments
Open

Comments

@night-angel-star
Copy link

Hi.
Thank you for maintaining this project.
I'm writing you to send my code implemented request the screen recording permission on android then start recording at anytime user want.

package com.isvisoft.flutter_screen_recording

import android.app.Activity
import android.content.Context
import android.content.Intent
import android.hardware.display.DisplayManager
import android.hardware.display.VirtualDisplay
import android.media.MediaRecorder
import android.media.projection.MediaProjection
import android.media.projection.MediaProjectionManager
import android.os.Build
import android.util.DisplayMetrics
import android.util.Log
import androidx.core.app.ActivityCompat
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.PluginRegistry
import java.io.IOException

import com.foregroundservice.ForegroundService
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding


class FlutterScreenRecordingPlugin() : MethodCallHandler, PluginRegistry.ActivityResultListener, FlutterPlugin, ActivityAware {

    var mScreenDensity: Int = 0
    var mMediaRecorder: MediaRecorder? = null
    var mProjectionManager: MediaProjectionManager? = null
    var mMediaProjection: MediaProjection? = null
    var mMediaProjectionCallback: MediaProjectionCallback? = null
    var mVirtualDisplay: VirtualDisplay? = null
    var mDisplayWidth: Int = 1280
    var mDisplayHeight: Int = 800
    var videoName: String? = ""
    var mFileName: String? = ""
    var recordAudio: Boolean? = false;
    var permissionAsked: Boolean = false;
    private val SCREEN_RECORD_REQUEST_CODE = 333

    private lateinit var _result: MethodChannel.Result

    var pluginBinding: FlutterPlugin.FlutterPluginBinding? = null
    var activityBinding: ActivityPluginBinding? = null;


    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean {
        if (requestCode == SCREEN_RECORD_REQUEST_CODE) {
            if (resultCode == Activity.RESULT_OK) {
                mMediaProjectionCallback = MediaProjectionCallback()
                mMediaProjection = mProjectionManager?.getMediaProjection(resultCode, data!!)
                mMediaProjection?.registerCallback(mMediaProjectionCallback!!, null)
                mVirtualDisplay = createVirtualDisplay()
                if(mVirtualDisplay==null){
                    _result.success(false)
                    return false
                }
                permissionAsked=true
                _result.success(true)
                return true
            } else {
                _result.success(false)
            }
        }
        return false
    }

    override fun onMethodCall(call: MethodCall, result: Result) {
        val appContext = pluginBinding!!.applicationContext

        when (call.method) {
            "requestPermission" -> {
                try {
                    _result = result
                    var title = call.argument<String?>("title")
                    var message = call.argument<String?>("message")
                    if (title == null || title == "") {
                        title = "Your screen is being recorded";
                    }
                    if (message == null || message == "") {
                        message = "Your screen is being recorded"
                    }
                    ForegroundService.startService(appContext, title, message)
                    mProjectionManager =
                        appContext.getSystemService(
                            Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager?
                    

                    val metrics = DisplayMetrics()
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                        mMediaRecorder = MediaRecorder(appContext)
                    } else {
                        @Suppress("DEPRECATION")
                        mMediaRecorder = MediaRecorder()
                    }


                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                        val display = activityBinding!!.activity.display
                        display?.getRealMetrics(metrics)
                    } else {
                        val defaultDisplay = appContext.getDisplay()
                        defaultDisplay?.getMetrics(metrics)
                    }
                    mScreenDensity = metrics.densityDpi
                    calculeResolution(metrics)
                    videoName = call.argument<String?>("name")
                    recordAudio = call.argument<Boolean?>("audio")

                    mFileName = pluginBinding!!.applicationContext.externalCacheDir?.absolutePath
                mFileName += "/$videoName.mp4"

                    mMediaRecorder?.setVideoSource(MediaRecorder.VideoSource.SURFACE)
                if (recordAudio!!) {
                    mMediaRecorder?.setAudioSource(MediaRecorder.AudioSource.MIC);
                    mMediaRecorder?.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
                    mMediaRecorder?.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
                } else {
                    mMediaRecorder?.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
                }
                mMediaRecorder?.setOutputFile(mFileName)
                mMediaRecorder?.setVideoSize(mDisplayWidth, mDisplayHeight)
                mMediaRecorder?.setVideoEncoder(MediaRecorder.VideoEncoder.H264)
                mMediaRecorder?.setVideoEncodingBitRate(5 * mDisplayWidth * mDisplayHeight)
                mMediaRecorder?.setVideoFrameRate(30)
                mMediaRecorder?.prepare()


                val permissionIntent = mProjectionManager?.createScreenCaptureIntent()
                ActivityCompat.startActivityForResult(
                    activityBinding!!.activity!!,
                    permissionIntent!!,
                    SCREEN_RECORD_REQUEST_CODE,
                    null
                )

                } catch (e: Exception) {
                    println("Error onMethodCall requestPermission")
                    println(e.message)
                    e.printStackTrace()
                    result.success(false)
                }
                
            }
            "startRecordScreen" -> {
                try {
                    if(!permissionAsked){
                        _result = result
                    var title = call.argument<String?>("title")
                    var message = call.argument<String?>("message")
                    if (title == null || title == "") {
                        title = "Your screen is being recorded";
                    }
                    if (message == null || message == "") {
                        message = "Your screen is being recorded"
                    }
                    ForegroundService.startService(appContext, title, message)
                    mProjectionManager =
                        appContext.getSystemService(
                            Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager?
                    

                    val metrics = DisplayMetrics()
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                        mMediaRecorder = MediaRecorder(appContext)
                    } else {
                        @Suppress("DEPRECATION")
                        mMediaRecorder = MediaRecorder()
                    }

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                        val display = activityBinding!!.activity.display
                        display?.getRealMetrics(metrics)
                    } else {
                        val defaultDisplay = appContext.getDisplay()
                        defaultDisplay?.getMetrics(metrics)
                    }
                    mScreenDensity = metrics.densityDpi
                    calculeResolution(metrics)
                    videoName = call.argument<String?>("name")
                    recordAudio = call.argument<Boolean?>("audio")
                    }
                    
                    startRecordScreen()

                } catch (e: Exception) {
                    println("Error onMethodCall startRecordScreen")
                    println(e.message)
                    result.success(false)
                }
            }
            "stopRecordScreen" -> {
                try {
                    ForegroundService.stopService(appContext)
                    if (mMediaRecorder != null) {
                        stopRecordScreen()
                        result.success(mFileName)
                    } else {
                        result.success("")
                    }
                } catch (e: Exception) {
                    result.success("")
                }
            }
            else -> {
                result.notImplemented()
            }
        }
    }

    private fun calculeResolution(metrics: DisplayMetrics) {

        mDisplayHeight = metrics.heightPixels
        mDisplayWidth = metrics.widthPixels

        // var maxRes = 1280.0;
        // if (metrics.scaledDensity >= 3.0f) {
        //     maxRes = 1920.0;
        // }
        // if (metrics.widthPixels > metrics.heightPixels) {
        //     var rate = metrics.widthPixels / maxRes

        //     if (rate > 1.5) {
        //         rate = 1.5
        //     }
        //     mDisplayWidth = maxRes.toInt()
        //     mDisplayHeight = (metrics.heightPixels / rate).toInt()
        //     println("Rate : $rate")
        // } else {
        //     var rate = metrics.heightPixels / maxRes
        //     if (rate > 1.5) {
        //         rate = 1.5
        //     }
        //     mDisplayHeight = maxRes.toInt()
        //     mDisplayWidth = (metrics.widthPixels / rate).toInt()
        //     println("Rate : $rate")
        // }

        println("Scaled Density")
        println(metrics.scaledDensity)
        println("Original Resolution ")
        println(metrics.widthPixels.toString() + " x " + metrics.heightPixels)
        println("Calcule Resolution ")
        println("$mDisplayWidth x $mDisplayHeight")
    }

    fun startRecordScreen() {
        try {
            if(!permissionAsked){
                val permissionIntent = mProjectionManager?.createScreenCaptureIntent()
                ActivityCompat.startActivityForResult(
                    activityBinding!!.activity!!,
                    permissionIntent!!,
                    SCREEN_RECORD_REQUEST_CODE,
                    null
                )
            }
            else{
                // _result.success(true)

            }
            
            try {
                mFileName = pluginBinding!!.applicationContext.externalCacheDir?.absolutePath
                mFileName += "/$videoName.mp4"
            } catch (e: IOException) {
                println("Error creating name")
                return
            }
            if(!permissionAsked){
                mMediaRecorder?.setVideoSource(MediaRecorder.VideoSource.SURFACE)
                if (recordAudio!!) {
                    mMediaRecorder?.setAudioSource(MediaRecorder.AudioSource.MIC);
                    mMediaRecorder?.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
                    mMediaRecorder?.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
                } else {
                    mMediaRecorder?.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
                }
                mMediaRecorder?.setOutputFile(mFileName)
                mMediaRecorder?.setVideoSize(mDisplayWidth, mDisplayHeight)
                mMediaRecorder?.setVideoEncoder(MediaRecorder.VideoEncoder.H264)
                mMediaRecorder?.setVideoEncodingBitRate(5 * mDisplayWidth * mDisplayHeight)
                mMediaRecorder?.setVideoFrameRate(30)
                mMediaRecorder?.prepare()
    
            }
            
            
            mMediaRecorder?.start()

            

        } catch (e: Exception) {
            Log.d("--INIT-RECORDER", e.message + "")
            println("Error startRecordScreen")
            println(e.message)
        }

    }

    fun stopRecordScreen() {
        try {
            println("stopRecordScreen")
            mMediaRecorder?.stop()
            mMediaRecorder?.reset()
            println("stopRecordScreen success")

        } catch (e: Exception) {
            Log.d("--INIT-RECORDER", e.message + "")
            println("stopRecordScreen error")
            println(e.message)

        } finally {
            stopScreenSharing()
        }
    }

    private fun createVirtualDisplay(): VirtualDisplay? {
        try {
            Log.d("FlutterScreenRecording", "Creating Virtual Display...")
            Log.d("FlutterScreenRecording", "Width: $mDisplayWidth, Height: $mDisplayHeight, Density: $mScreenDensity")
    
            return mMediaProjection?.createVirtualDisplay(
                "ScreenRecording",
                mDisplayWidth,
                mDisplayHeight,
                mScreenDensity,
                DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                mMediaRecorder?.surface,
                null,
                null
            )
        } catch (e: Exception) {
            Log.e("FlutterScreenRecording", "createVirtualDisplay failed!", e)
            e.printStackTrace()
            return null
        }
    }
    

    private fun stopScreenSharing() {
        if (mVirtualDisplay != null) {
            mVirtualDisplay?.release()
            if (mMediaProjection != null && mMediaProjectionCallback != null) {
                mMediaProjection?.unregisterCallback(mMediaProjectionCallback!!)
                mMediaProjection?.stop()
                mMediaProjection = null
            }
            Log.d("TAG", "MediaProjection Stopped")
        }
    }

    inner class MediaProjectionCallback : MediaProjection.Callback() {
        override fun onStop() {
            mMediaRecorder?.reset()
            mMediaProjection = null
            stopScreenSharing()
        }
    }

    override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
        pluginBinding = binding;
    }

    override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {}

    override fun onAttachedToActivity(binding: ActivityPluginBinding) {
        activityBinding = binding;
        val channel = MethodChannel(pluginBinding!!.binaryMessenger, "flutter_screen_recording")
        channel.setMethodCallHandler(this)
        activityBinding!!.addActivityResultListener(this);
    }

    override fun onDetachedFromActivityForConfigChanges() {}

    override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
        activityBinding = binding;
    }

    override fun onDetachedFromActivity() {}
}

And I think we can add pause/resume feature using ffmpeg kit.
I hope my code helped you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant