I am trying to take a screenshot of the displayed activity.
In this case, the activity contains a webview (wvMainView).
The problem is, the main content of the screen (usually a chart), does not appear in the screenshot. The only time I get the full screenshot is when I have a table inside the webpage.
Here is the code for the screenshot:
var lMainActivityLayout: ConstraintLayout? = findViewById(R.id.lMainActivityLayout)
val bitmap = getScreenShotFromView(lMainActivityLayout!!)
// val bitmap = getScreenShotFromView(wvMainView!!)
if (bitmap != null){ saveMediaToStorage(bitmap) }
private fun getScreenShotFromView(v: View): Bitmap?
{
Log.i("-","MainActivity > getScreenShotFromView")
var screenshot: Bitmap? = null
try
{
screenshot = Bitmap.createBitmap(v.measuredWidth, v.measuredHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(screenshot)
v.draw(canvas)
}
catch (e: Exception)
{
Log.e("GFG", "Failed to capture screenshot because:" + e.message)
}
return screenshot
}
private fun saveMediaToStorage(bitmap: Bitmap)
{
Log.i("-","MainActivity > saveMediaToStorage")
val filename = "${System.currentTimeMillis()}.jpg"
var fos: OutputStream? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
{
this.contentResolver?.also { resolver ->
val contentValues = ContentValues().apply
{
put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg")
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
}
val imageUri: Uri? = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
fos = imageUri?.let { resolver.openOutputStream(it) }
}
}
else
{
val imagesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
val image = File(imagesDir, filename)
fos = FileOutputStream(image)
}
fos?.use
{
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, it)
Toast.makeText(this , "Image saved to Gallery!" , Toast.LENGTH_SHORT).show()
}
}
As for the screenshot, take a look at the example below. When I run the app and take the screenshot, inside the gray area there is a bar chart that simple won't show up in the screenshot.
I tried taking a screenshot of the main layout as well as of the web view but with the same result.
The iOS version of the app works fine.
Any idea on what causes this strange behavior?
Maybe I should take the screenshot of the entire screen and not of a certain view (is this possible)?
And another small issue - the screenshot does no always appear in the Gallery app although I can find it using the Files app.
I ended up using ScreenShotty for this - https://github.com/bolteu/screenshotty
Add this to build.graddle:
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
Here's the code, maybe it helps someone:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?)
{
Log.i("","MainActivity > onActivityResult")
super.onActivityResult(requestCode, resultCode, data)
screenshotManager!!.onActivityResult(requestCode, resultCode, data)
}
fun getFullScreenshot()
{
Log.i("","MainActivity > getFullScreenshot")
val screenshotResult = screenshotManager!!.makeScreenshot()
val subscription = screenshotResult.observe(
onSuccess =
{
// Add a delay to prefent lag / crash on Android 5.0/5.1.
// Not sure if this is the correct way but it works for me
Handler(Looper.getMainLooper()).postDelayed({ editScreenshot(it) }, 1000)
},
onError =
{
Log.i("", "Screenshot failed!")
}
)
}
fun editScreenshot(screenshot: Screenshot)
{
Log.i("","MainActivity > editScreenshot")
val width: Int = Resources.getSystem().getDisplayMetrics().widthPixels
val height: Int = Resources.getSystem().getDisplayMetrics().heightPixels
val bitmap = when (screenshot)
{
is ScreenshotBitmap -> screenshot.bitmap
}
// Multiple resolutions cases go here
bitmap?.apply {
cropRectangle(
xOffset = 50,
yOffset = 250,
newWidth = width - 100,
newHeight = height - 450
)?.let { saveMediaToStorage(it) }
}
}
fun saveMediaToStorage(bitmap: Bitmap)
{
Log.i("","MainActivity > saveMediaToStorage")
val screenshotFileName = "${System.currentTimeMillis()}.jpg"
var fos: OutputStream? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
{
this.contentResolver?.also { resolver ->
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, screenshotFileName)
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg")
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
}
val imageUri: Uri? = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
fos = imageUri?.let { resolver.openOutputStream(it) }
}
}
else
{
val imagesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
val image = File(imagesDir, screenshotFileName)
fos = FileOutputStream(image)
}
fos?.use {
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, it)
Toast.makeText(this , "Captured View and saved to Gallery" , Toast.LENGTH_SHORT).show()
}
}
Related
I implemented share image to other apps using intent, I followed the recommended approach by saving the image first then getting its LocalBitmapUri. but when i run the app and share image i get the Toast message [The file format is not supported]. and i cant seem to figure out what I did wrong.. thanks.
fun shareItem(url: String?,context: Context,scope: CoroutineScope) {
scope.launch {
withContext(Dispatchers.IO){
val i = Intent(Intent.ACTION_SEND)
i.type = "image/*"
i.putExtra(Intent.EXTRA_STREAM,
getBitmap("d",context,"https://cdn2.thecatapi.com/images/3o8.jpg")?.let {
getLocalBitmapUri(
it,
context
)
})
startActivity(context,Intent.createChooser( i, "Share Image"),null)
}
}
}
fun getLocalBitmapUri(bmp: Bitmap,context: Context): Uri? {
val builder = VmPolicy.Builder()
StrictMode.setVmPolicy(builder.build())
var bmpUri: Uri? = null
try {
val file = File(
context.getExternalFilesDir(Environment.DIRECTORY_PICTURES),
"share_image_jj" + System.currentTimeMillis() + ".png"
)
val out = FileOutputStream(file)
bmp.compress(Bitmap.CompressFormat.PNG, 90, out)
out.close()
bmpUri = Uri.fromFile(file)
} catch (e: IOException) {
e.printStackTrace()
}
return bmpUri
}
private suspend fun getBitmap(tag: String, context: Context, imageUrl: String): Bitmap? {
var bitmap: Bitmap? = null
val imageRequest = ImageRequest.Builder(context)
.data(imageUrl)
.target(
...//,
onSuccess = { result ->
Log.e(tag, "Coil loader success.")
bitmap = result.toBitmap()
}
)
.build()
context.imageLoader.execute(imageRequest)
return bitmap
}
I am working with Android version 10.
I have enabled Permissions to Read & Write Storage
Device Name : Poco F1
Scenario: I have to capture a screenshot of the current layout and save it to internalStorage and preview that image to the user. Here users have an option to delete the image.
Here are the codes I am using to save & delete
Saving a screenshot:
//I will pass the bitmap here
fun saveBitmapToInternalStorage(bitmap: Bitmap?) {
bitmap?.let {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
saveBitmapToOlderDevice(it)
} else {
saveBitmapToNewerDevice(it)
}
}
}
//This method is to save image to newerdevice >= Q
#RequiresApi(Build.VERSION_CODES.Q)
private fun saveBitmapToNewerDevice(bitmap: Bitmap) {
val uri = generateUri()
context.contentResolver.openOutputStream(uri ?: return).use { outputStream ->
outputStream?.let {
writeBitmapToJpeg(bitmap, outputStream, uri.toString())
}
}
}
//This is to generate the URI.
#RequiresApi(Build.VERSION_CODES.Q)
private fun generateUri(): Uri? {
val dateFormat = getDateFormat()
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, "${dateFormat}.jpg")
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
put(MediaStore.MediaColumns.RELATIVE_PATH, "Pictures/${context.resources.getString(R.string.app_name)}")
}
return context.contentResolver.insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues
)
}
// To save images to olderDevice
private fun saveBitmapToOlderDevice(bmp: Bitmap) {
val filename = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)?.absolutePath +
"/${context.resources.getString(R.string.app_name)}/${getDateFormat()}.jpg"
createDirectory(filename)
val outputStream = FileOutputStream(filename)
writeBitmapToJpeg(bmp, outputStream, filename)
}
//This method is to save the image to InternalStorage
private fun writeBitmapToJpeg(bmp: Bitmap, outputStream: OutputStream, imagePath: String) {
try {
val outputData = ByteArrayOutputStream()
bmp.compress(CompressFormat.JPEG, 100, outputData)
outputData.writeTo(outputStream)
outputStream.flush()
outputStream.close()
} catch (e: IOException) {
showBitmapWriteErrorMessage()
}
}
I save the path while storing the image in internalStorgae
the path looks like
/storage/emulated/0/Pictures/TGP AR/20211011142001.jpg
and i pass this path into below method
To delete the image :
private fun deleteImage(imagePath: String) {
val file = File(imagePath)
file.delete()
}
file.exists() is returning true.
file.delete() is returning false.
I think, there might be two different ways to delete ( > & < Q ).
Please help me
You can delete the image by modifying your method to the following:
private fun deleteImage(imagePath: Uri) {
getContentResolver().delete(imagePath, null, null)
}
Then pass the Uri created in generateUri() to delete the file.
i have a probleme, i want to create a pdf and save him in download path on the phone, but it seems like there is an error and i don't know how to figure it out.
Can anyone help me please ? (I already have the permission for access storage, i think :/ ).
This is my code -->
fun createPdf
fun createPdf(
text: String,
context: Context
) {
val myPDFdocument = PdfDocument()
val myPaint = Paint()
val myPageInfo1 = PdfDocument.PageInfo.Builder(1240, 1754, 1).create()
val myPage1 = myPDFdocument.startPage(myPageInfo1)
val canvas = myPage1.canvas
canvas.drawText(text, 40F, 50F, myPaint)
myPDFdocument.finishPage(myPage1)
val file = File(context.externalCacheDir!!.absolutePath, "/FirstPdf.pdf")
try {
myPDFdocument.writeTo(FileOutputStream(file))
} catch (e: IOException) {
e.printStackTrace()
}
myPDFdocument.close()
}
I found the problem, here is the solution : (Principaly for android 11)
fun createPdf
val imageCollection =
MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val contentValues = ContentValues().apply {
put(MediaStore.Downloads.DISPLAY_NAME, "${commande}.pdf")
put(MediaStore.Downloads.MIME_TYPE, "application/pdf")
put(MediaStore.Downloads.IS_PENDING, 1)
}
val itemUri = resolver.insert(imageCollection, contentValues)
try {
Toast.makeText(context, "salut toi", Toast.LENGTH_SHORT).show()
resolver.insert(imageCollection, contentValues)?.also { uri ->
resolver.openOutputStream(uri).use { outputStream ->
myPDFdocument.writeTo(outputStream)
}
}
contentValues.clear()
contentValues.put(MediaStore.Downloads.IS_PENDING, 0)
resolver.update(itemUri!!, contentValues, null, null)
myPDFdocument.close()
} catch (e: IOException) {
e.printStackTrace()
}
I'm creating a photo making app using Camera2 API.
I would want to set additional information to the photo (date, location), but I'm getting "Uknown URL" exception.
When i comment out contentResolver, photo is saved, but is lacking any additional information, and I need to have access to the location - I will be filtering gallery to only those which are in close proximity.
internal class ImageSaver(
private val image: Image,
private val file: File,
private val watermark: Bitmap,
private val mContext: Context
) : Runnable {
private val saveImageExecutor: Executor = Executors.newSingleThreadExecutor()
override fun run() {
val jpegByteBuffer = image.planes[0].buffer
val jpegByteArray = ByteArray(jpegByteBuffer.remaining())
jpegByteBuffer.get(jpegByteArray)
val width = image.width
val height = image.height
saveImageExecutor.execute {
val date = System.currentTimeMillis()
val location = getLocation(mContext)
val longitude = location?.longitude ?: 0.0
val latitude = location?.latitude ?: 0.0
// watermark
val options = BitmapFactory.Options()
options.inMutable = true
val original =
BitmapFactory.decodeByteArray(jpegByteArray, 0, jpegByteArray.size, options)
val overlayed = overlay(original, watermark)
val watermarkedByteArrayOS = ByteArrayOutputStream()
overlayed!!.compress(Bitmap.CompressFormat.JPEG, 100, watermarkedByteArrayOS)
val watermarkedByteArray = watermarkedByteArrayOS.toByteArray()
Log.d(TAG, "saving pic meta-data")
val values = ContentValues()
values.put(MediaStore.Images.ImageColumns.TITLE, file.name)
values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, file.name)
values.put(MediaStore.Images.ImageColumns.DATA, file.path)
values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, date)
values.put(MediaStore.Images.ImageColumns.WIDTH, width)
values.put(MediaStore.Images.ImageColumns.HEIGHT, height)
values.put(MediaStore.Images.ImageColumns.LONGITUDE, longitude)
values.put(MediaStore.Images.ImageColumns.LATITUDE, latitude)
Log.d(TAG, "LON: ${values.get(MediaStore.Images.ImageColumns.LATITUDE)}")
Log.d(TAG, "LAT: ${values.get(MediaStore.Images.ImageColumns.LONGITUDE)}")
var output: FileOutputStream? = null
try {
output = FileOutputStream(file).apply {
write(watermarkedByteArray)
}
} catch (e: IOException) {
Log.e(TAG, e.toString())
} finally {
image.close()
output?.let {
try {
it.close()
} catch (e: IOException) {
Log.e(TAG, e.toString())
}
}
}
mContext.contentResolver.insert(Uri.fromFile(file), values)
}
}
Output:
java.lang.IllegalArgumentException: Unknown URL file:///storage/emulated/0/Android/data/(...)/DCIM/20200610165428492.jpg
at android.content.ContentResolver.insert(ContentResolver.java:1831)
at ...ImageSaver$run$1.run(ImageSaver.kt:86)
What should be the URI? Is there any better way to store location of a photo?
In Android Studio I want to save a BitMap to a specific folder in the galery of the android device for example /test_pictures as an image.
The easy ways I found on the internet seem to be all deprecated, so it is not good practice to use those.
Does anyone have an easy example code on how to achieve this in Kotlin?
Kotlin bitmap extension like this:
fun Bitmap.saveImage(context: Context): Uri? {
if (android.os.Build.VERSION.SDK_INT >= 29) {
val values = ContentValues()
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
values.put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis() / 1000)
values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis())
values.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/test_pictures")
values.put(MediaStore.Images.Media.IS_PENDING, true)
values.put(MediaStore.Images.Media.DISPLAY_NAME, "img_${SystemClock.uptimeMillis()}")
val uri: Uri? =
context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
if (uri != null) {
saveImageToStream(this, context.contentResolver.openOutputStream(uri))
values.put(MediaStore.Images.Media.IS_PENDING, false)
context.contentResolver.update(uri, values, null, null)
return uri
}
} else {
val directory =
File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES).toString() + separator + "test_pictures")
if (!directory.exists()) {
directory.mkdirs()
}
val fileName = "img_${SystemClock.uptimeMillis()}"+ ".jpeg"
val file = File(directory, fileName)
saveImageToStream(this, FileOutputStream(file))
if (file.absolutePath != null) {
val values = contentValues()
values.put(MediaStore.Images.Media.DATA, file.absolutePath)
// .DATA is deprecated in API 29
context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
return Uri.fromFile(file)
}
}
return null
}
fun saveImageToStream(bitmap: Bitmap, outputStream: OutputStream?) {
if (outputStream != null) {
try {
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
outputStream.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
}
val imageUri = YourBitmap.saveImage(applicationContext)