I would like for it to take just one screenshot.
P.S. I converted your code to kotlin and integrated it with Flutter. I am not a native developer, but I do understand a lot of things about the code but maybe I am overlooking something.
package com.example.native_example
import android.annotation.SuppressLint
import android.app.Activity
import android.app.Service
import android.app.Notification
import android.content.ContentValues.TAG
import android.content.Context
import android.content.Intent
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.ImageFormat
import android.graphics.PixelFormat
import android.hardware.display.DisplayManager
import android.hardware.display.VirtualDisplay
import android.media.Image
import android.media.ImageReader
import android.media.ImageReader.OnImageAvailableListener
import android.media.projection.MediaProjection
import android.media.projection.MediaProjectionManager
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.util.Log
import android.view.Display
import android.view.OrientationEventListener
import android.view.WindowManager
import com.example.native_example.NotificationUtils.getNotification
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.nio.ByteBuffer
import java.util.*
class CaptureSerivce : Service() {
private val TAG = "ScreenCaptureService"
private val RESULT_CODE = "RESULT_CODE"
private val DATA = "DATA"
private val ACTION = "ACTION"
private val START = "START"
private val STOP = "STOP"
private val SCREENCAP_NAME = "screencap"
private var IMAGES_PRODUCED = 0
private var mMediaProjection: MediaProjection? = null
private var mStoreDir: String? = null
private var mImageReader: ImageReader? = null
private var mHandler: Handler? = null
private var mDisplay: Display? = null
private var mVirtualDisplay: VirtualDisplay? = null
private var mDensity = 0
private var mWidth = 0
private var mHeight = 0
private var mRotation = 0
private var mOrientationChangeCallback: OrientationChangeCallback? = null
fun getStartIntent(context: Context?, resultCode: Int, data: Intent?): Intent? {
val intent = Intent(context, CaptureSerivce::class.java)
intent.putExtra(ACTION, START)
intent.putExtra(RESULT_CODE, resultCode)
intent.putExtra(DATA, data)
return intent
}
fun getStopIntent(context: Context?): Intent? {
val intent = Intent(context, CaptureSerivce::class.java)
intent.putExtra(ACTION, STOP)
return intent
}
private fun isStartCommand(intent: Intent): Boolean {
return (intent.hasExtra(RESULT_CODE) && intent.hasExtra(DATA)
&& intent.hasExtra(ACTION) && Objects.equals(intent.getStringExtra(ACTION), START))
}
private fun isStopCommand(intent: Intent): Boolean {
return intent.hasExtra(ACTION) && Objects.equals(intent.getStringExtra(ACTION), STOP)
}
private fun getVirtualDisplayFlags(): Int {
return DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY or DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
}
inner class ImageAvailableListener : OnImageAvailableListener {
override fun onImageAvailable(reader: ImageReader) {
try {
mImageReader?.acquireLatestImage().use { image ->
if (image != null) {
val planes: Array<Image.Plane> = image.getPlanes()
val buffer: ByteBuffer = planes[0].getBuffer()
val pixelStride: Int = planes[0].getPixelStride()
val rowStride: Int = planes[0].getRowStride()
val rowPadding: Int = rowStride - pixelStride * mWidth
// create bitmap
val bitmap = Bitmap.createBitmap(
mWidth + rowPadding / pixelStride,
mHeight,
Bitmap.Config.ARGB_8888
)
bitmap.copyPixelsFromBuffer(buffer)
// write bitmap to a file
val fos = FileOutputStream("$mStoreDir/myscreen_$IMAGES_PRODUCED.png")
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos)
IMAGES_PRODUCED++
Log.e(TAG, "captured image: $IMAGES_PRODUCED")
try {
fos.close()
} catch (ioe: IOException) {
ioe.printStackTrace()
}
if (bitmap != null) {
bitmap!!.recycle()
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
inner class OrientationChangeCallback internal constructor(context: Context?) :
OrientationEventListener(context) {
override fun onOrientationChanged(orientation: Int) {
val rotation: Int = mDisplay!!.getRotation()
if (rotation != mRotation) {
mRotation = rotation
try {
// clean up
mVirtualDisplay?.release()
mImageReader?.setOnImageAvailableListener(null, null)
// re-create virtual display depending on device width / height
createVirtualDisplay()
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
}
}
}
inner class MediaProjectionStopCallback : MediaProjection.Callback() {
override fun onStop() {
Log.e(TAG, "stopping projection.")
mHandler!!.post(Runnable {
mVirtualDisplay?.release()
mImageReader?.setOnImageAvailableListener(null, null)
mOrientationChangeCallback?.disable()
mMediaProjection?.unregisterCallback(this@MediaProjectionStopCallback)
})
}
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onCreate() {
super.onCreate()
// create store dir
val externalFilesDir: File? = getExternalFilesDir(null)
if (externalFilesDir != null) {
mStoreDir = externalFilesDir.getAbsolutePath().toString() + "/screenshots/"
val storeDirectory = File(mStoreDir)
if (!storeDirectory.exists()) {
val success: Boolean = storeDirectory.mkdirs()
if (!success) {
Log.e(TAG, "failed to create file storage directory.")
stopSelf()
}
}
} else {
Log.e(TAG, "failed to create file storage directory, getExternalFilesDir is null.")
stopSelf()
}
// start capture handling thread
object : Thread() {
override fun run() {
// Looper.prepare()
// mHandler = Handler()
// Looper.loop()
}
}.start()
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
when {
isStartCommand(intent) -> {
// create notification
val pair = NotificationUtils.getNotification(this)
val first = pair.first
val second = pair.second
startForeground(first, second)
// start projection
val resultCode = intent.getIntExtra(RESULT_CODE, Activity.RESULT_CANCELED)
val data = intent.getParcelableExtra<Intent>(DATA)
startProjection(resultCode, data)
}
isStopCommand(intent) -> {
stopProjection()
stopSelf()
}
else -> {
stopSelf()
}
}
return START_NOT_STICKY
}
private fun startProjection(resultCode: Int, data: Intent?) {
val mpManager = getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
if (mMediaProjection == null) {
mMediaProjection = mpManager.getMediaProjection(resultCode, data!!)
if (mMediaProjection != null) {
// display metrics
mDensity = Resources.getSystem().getDisplayMetrics().densityDpi
val windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
mDisplay = windowManager.defaultDisplay
// create virtual display depending on device width / height
createVirtualDisplay()
// register orientation change callback
mOrientationChangeCallback = OrientationChangeCallback(this)
if (mOrientationChangeCallback!!.canDetectOrientation()) {
mOrientationChangeCallback!!.enable()
}
// register media projection stop callback
mMediaProjection!!.registerCallback(MediaProjectionStopCallback(), mHandler)
}
}
}
private fun stopProjection() {
mHandler?.post(Runnable {
mMediaProjection?.stop()
})
}
private fun createVirtualDisplay() {
// get width and height
mWidth = Resources.getSystem().getDisplayMetrics().widthPixels
mHeight = Resources.getSystem().getDisplayMetrics().heightPixels
// start capture reader
mImageReader = ImageReader.newInstance(mWidth, mHeight, ImageFormat.YV12, 2)
mVirtualDisplay = mMediaProjection!!.createVirtualDisplay(
SCREENCAP_NAME, mWidth, mHeight,
mDensity, getVirtualDisplayFlags(), mImageReader!!.surface, null, mHandler
)
mImageReader!!.setOnImageAvailableListener(ImageAvailableListener(), mHandler)
}
}
import android.content.*
import android.media.projection.MediaProjectionManager
import android.util.Log
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity: FlutterActivity() {
private val REQUEST_CODE = 100
private val CHANNEL = "native_channel"
private lateinit var channel: MethodChannel
private lateinit var mediaProjectionManager : MediaProjectionManager
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
channel.setMethodCallHandler { call, result ->
if(call.method == "test"){
startProjection();
stopProjection();
result.success("Zeeshan was Successful")
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_CODE) {
if (resultCode == RESULT_OK) {
startService(
CaptureSerivce().getStartIntent(this, resultCode, data)
)
Log.d("", "Service started")
}
}
}
private fun startProjection() {
val mProjectionManager =
getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
startActivityForResult(mProjectionManager.createScreenCaptureIntent(), REQUEST_CODE)
}
private fun stopProjection() {
startService(CaptureSerivce().getStopIntent(this))
}
}
Would appreciate your help. TIA