Compress Image bytes in Xamarin.Android - android

How can be compress the image taken in Xamarin.Android using CameraSourcePreview, the byte in the method OnPictureTaken is too big.

Here's one way to do it by converting the Bitmap to a compressed JPG file. Also in this example is how to save the compressed JPG file to the picture gallery and make it immediately available through USB/Windows via an Android Media Scan. Hope this helps!
public void OnPictureTaken(byte[] data, Camera camera) {
var bmp = BitmapFactory.DecodeByteArray(data, 0, data.Length);
SaveBitmapAsJPEG(bmp, 75);
}
public void SaveBitmapAsJPEG(Bitmap pBitmap, int pnQuality = 85) {
Java.IO.File jFolder = GetCreatePhotoAlbumStorageDir("MyPhotoAlbum");
Java.IO.File jFile = new Java.IO.File(jFolder, $"Photo_{DateTime.Now.ToString("yyyyMMddHHmmss")}.jpg");
// "/storage/emulated/0/Pictures/MyPhotoAlbum/Photo_20190526112410.jpg", which is the following via Windows/USB...
// "Internal shared storage\Pictures\MyPhotoAlbum\Photo_20190526112410.jpg"
using (var fs = new FileStream(jFile.AbsolutePath, FileMode.CreateNew)) {
pBitmap.Compress(Bitmap.CompressFormat.Jpeg, pnQuality, fs);
}
SavePictureToGallery(jFile);
Android.Util.Log.Info("MyApp", $"Picture saved using SaveBitmapAsJPEG() at {jFile.AbsolutePath}");
// Request the media scanner to scan a file and add it to the media database (Make file visible/available through USB connection in Windows Explorer)
var f = new Java.IO.File(jFile.AbsolutePath);
var intent = new Intent(Intent.ActionMediaScannerScanFile);
intent.SetData(Android.Net.Uri.FromFile(f));
Application.Context.SendBroadcast(intent);
}
public Java.IO.File GetCreatePhotoAlbumStorageDir(string psAlbumName) {
// Get the directory for the user's public pictures directory. Will create if it doesn't exist.
var dir = new Java.IO.File(Environment.GetExternalStoragePublicDirectory(Environment.DirectoryPictures), psAlbumName);
if (!dir.Exists())
dir.Mkdirs();
return dir;
}
private void SavePictureToGallery(Java.IO.File pFile) {
var intent = new Intent(MediaStore.ActionImageCapture);
intent.PutExtra(MediaStore.ExtraOutput, Android.Net.Uri.FromFile(pFile));
StartActivityForResult(intent, 0);
}
Note that you could change the format to PNG if desired by changing the "CompressFormat" to .Png, and naming the file accordingly.

Related

Xamarin.Forms move image to gallery in Android 12 and above

I know that is easy to take a photo and save it to Gallery.
protected async Task<MediaFile> TakePhoto()
{
var storageOptions = new StoreCameraMediaOptions()
{
SaveToAlbum = true,
Directory = pictureAlbumName,
Name = $"test_{DateTime.Now.ToString("HH_mm_ss_ff")}.jpg"
};
return await CrossMedia.Current.TakePhotoAsync(storageOptions);
}
As the result I got the URL that looks like this:
/storage/emulated/0/Android/data/com.companyname.appname/files/Pictures/MyAlbum/photo_18_47_29_69.jpg
But when I tried to save the image from bytes it appears in the folder but never appears in the gallery. After saving the image I tried of course to scan the newly created path but there was no effect
First attempt
File.WriteAllBytes("/storage/emulated/0/Android/data/com.companyname.appname/files/Pictures/MyAlbum/downloaded_image_223213a3as.jpg", immageBytes);
MediaScannerConnection.ScanFile(Application.Context, new string[] { path },null,null);
Second attempt using obsoleted Android methods
Java.IO.File storagePath = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures);
string path = System.IO.Path.Combine(storagePath.ToString(), filename);
System.IO.File.WriteAllBytes(path, imageByte);
var mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
mediaScanIntent.SetData(Android.Net.Uri.FromFile(new Java.IO.File(path)));
CurrentContext.SendBroadcast(mediaScanIntent);
Update:
Basically you need to use this method and save it
private void SaveImageToStorage(Bitmap bitmap)
{
Stream imageOutStream;
if (Build.VERSION.SdkInt >= BuildVersionCodes.Q)
{
ContentValues values = new ContentValues();
values.Put(MediaStore.IMediaColumns.DisplayName,
"image_screenshot.jpg");
values.Put(MediaStore.IMediaColumns.MimeType, "image/jpeg");
values.Put(MediaStore.IMediaColumns.RelativePath,
Android.OS.Environment.DirectoryPictures + Java.IO.File.PathSeparator + "AppName");
Android.Net.Uri uri = this.ContentResolver.Insert(MediaStore.Images.Media.ExternalContentUri, values);
imageOutStream = ContentResolver.OpenOutputStream(uri);
}
else
{
String imagesDir =Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures).ToString() + "/AppName";
imageOutStream = File.OpenRead(System.IO.Path.Combine(imagesDir, "image_screenshot.jpg"));
}
bitmap.Compress(Bitmap.CompressFormat.Jpeg, 100, imageOutStream);
imageOutStream.Close();
}
OG Answer:
As far as I know, Only images in your media store provider are visible to your gallery and to add it to the media store you need to use the following:
MediaStore.Images.Media.InsertImage(Activity.ContentResolver, ImgBitmap, yourTitle , yourDescription);
Hope this helps :)

Xamarin.Android Save Image to Gallery Photo Album

I need to save a bitmap to the Gallery using my application name as the album. The path should be:
Gallery->MyAppName->test.png
but the best result that I get looks like:
Gallery->Others->MyAppName->test.png
Here is my code:
using Android.Graphics;
using Android.Media;
using System;
using System.IO;
..
.
.
public static void ExportBitmapAsPNG(Bitmap bitmap)
{
var sdCardPath = Android.OS.Environment.ExternalStorageDirectory.AbsolutePath+"/MyAppName";
if (!Directory.Exists(sdCardPath))
Directory.CreateDirectory(sdCardPath);
var filePath = System.IO.Path.Combine(sdCardPath, "test.png");
var stream = new FileStream(filePath, FileMode.Create);
bitmap.Compress(Bitmap.CompressFormat.Png, 100, stream);
stream.Close();
MediaScannerConnection.ScanFile(Android.App.Application.Context, new string[] { filePath }, null, null);
}
I hope someone could tell me what I'm missing?
P.S.
I tried to use MediaStore, but it's always save in Picture folder and has no overloads to change this.
Android.Provider.MediaStore.Images.Media.InsertImage(ContentResolver, bitmap2, "test", "");
I've rewritten your function to provide that capability. The key difference is how the folder is created. Additionally, I use an Intent for the media scanner. Anyway, I think this should get you where you want to be. Hope this helps!
public void ExportBitmapAsPNG(Bitmap bitmap) {
// Get/Create Album Folder To Save To
var jFolder = new Java.IO.File(Environment.GetExternalStoragePublicDirectory(Environment.DirectoryPictures), "MyAppNamePhotoAlbum");
if (!jFolder.Exists())
jFolder.Mkdirs();
var jFile = new Java.IO.File(jFolder, "MyPhoto.jpg");
// Save File
using (var fs = new FileStream(jFile.AbsolutePath, FileMode.CreateNew)) {
bitmap.Compress(Bitmap.CompressFormat.Png, 100, fs);
}
// Save Picture To Gallery
var intent = new Intent(MediaStore.ActionImageCapture);
intent.PutExtra(MediaStore.ExtraOutput, Android.Net.Uri.FromFile(jFile));
StartActivityForResult(intent, 0);
// Request the media scanner to scan a file and add it to the media database
var f = new Java.IO.File(jFile.AbsolutePath);
intent = new Intent(Intent.ActionMediaScannerScanFile);
intent.SetData(Android.Net.Uri.FromFile(f));
Application.Context.SendBroadcast(intent);
}
Hope this helps!

Save Screenshot into Internal Storage and Sync into Gallery?

I have searched high and low for a way to do this and the best I could find involved saving the screenshot into the SD Card. What I want to do instead is to onclick(), take a screenshot of the current activity and saves it in the internal storage so that the user can view it in their gallery as and when they want.
Any help is greatly appreciated.
It is harder to post a whole code in here. I think you should follow some tutorials.
According to the your requirement what I got is, You need to take a screenshot using an your application and it should be stored in device SD card.
For that you should add proper permission to the manifest first,
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
and add following code to the activity:
private void takeScreenshot() {
Date now = new Date();
android.text.format.DateFormat.format("yyyy-MM-dd_hh:mm:ss", now);
try {
// image naming and path to include sd card appending name you choose for file, you can change it to your path
String mPath = Environment.getExternalStorageDirectory().toString() + "/" + now + ".jpg";
// create bitmap screen capture
View v1 = getWindow().getDecorView().getRootView();
v1.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(v1.getDrawingCache());
v1.setDrawingCacheEnabled(false);
File imageFile = new File(mPath);
FileOutputStream outputStream = new FileOutputStream(imageFile);
int quality = 100;
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);
outputStream.flush();
outputStream.close();
openScreenshot(imageFile);
} catch (Throwable e) {
e.printStackTrace();
}
}
this code will open the generated image(screenshot):
private void openScreenshot(File imageFile) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
Uri uri = Uri.fromFile(imageFile);
intent.setDataAndType(uri, "image/*");
startActivity(intent);
}
This is how I did for my project. Sometimes this might be not satisfied for you requirement. It it is not satisfied, please follow these tutorials, Thanks
Reference List : http://www.androhub.com/take-a-screenshot-programmatically-in-android/
http://devdeeds.com/take-screenshot-programmatically/
https://www.viralandroid.com/2016/01/how-to-take-screenshot-programmatically-in-android.html
if you want to check whether SD card is available or not. here is the way. If SD card is not available then you can use internal storage to store the image.
Boolean isSDPresent = android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED);
Boolean isSDSupportedDevice = Environment.isExternalStorageRemovable();
if(isSDSupportedDevice && isSDPresent)
{
// yes SD-card is present
}
else
{
// SD-card not available
}

xamarin form app file not saving on device storage

i have created xamarin form app for signpad which capture sign and saving png file to device storage but file is not writing on storage
here is my code to convert image to bytes[]
var image = await signature.GetImageStreamAsync(SignaturePad.Forms.SignatureImageFormat.Png);//getting png file from here
var signatureMemoryStream = image as MemoryStream;
byte[] data = signatureMemoryStream.ToArray();// convert png to bytes[]
string fileName = "img.png";
DependencyService.Get<IFileReadWrite>().WriteData(fileName, data);
I have created DependencyService (Interface) for saving file
public interface IFileReadWrite
{
void WriteData(string fileName, byte[] data);
}
This is my code to save file using native(app.android) api
public class FileHelper : IFileReadWrite
{
public void WriteData(string filename, byte[] data)
{
var documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
var filePath = Path.Combine(documentsPath, filename);
File.WriteAllBytes(filePath, data); // this execute without error but file is not saving on path
}
}
i already have given permission WRITE_EXTERNAL_STORAGE in mainfest
Replace this in your code and try:
var documentsPath = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, Android.OS.Environment.DirectoryDownloads);
Hope this may solve your issue.

camera image not showing recently taken photo to select in Camera folder

I am using Android 7.0 mobile device for my testing.
I am implementing an android app.
I need to take a photo from device camera and just select it from camera folder/ Gallery using by the application.
But after taking a photo and go back to my application and try to select that captured photo by my app, when moving to camera folder, the recently taken photo is not in the gallery.
But I noticed when I take another photo and go to select that photo from again via my app, from the camera folder, I can see the photo I took previously but not the recently taken.
I followed this answer - Android: Refreshing the Gallery after saving new images. But it does not work for me.
If somebody can reply with the answer and that answer contains with a file path to find, please add those details also. ex:- "how to take android camera image gallery path"
The code I use here:-
val cursor: Cursor? = contentResolver.query(uri, projectionColumns, null, null, sortOrder)
if (cursor != null && cursor.moveToFirst()) {
while (cursor.moveToNext()) {
// Get values per item
val imageId = cursor.getString(cursor.getColumnIndex(projectionColumns[0]))
val imageName = cursor.getString(cursor.getColumnIndex(projectionColumns[1]))
val imagePath = cursor.getString(cursor.getColumnIndex(projectionColumns[2]))
val dateTaken = cursor.getString(cursor.getColumnIndex(projectionColumns[3]))
val imageSize = cursor.getString(cursor.getColumnIndex(projectionColumns[4]))
val bucketId = cursor.getString(cursor.getColumnIndex(projectionColumns[5]))
val bucketName = cursor.getString(cursor.getColumnIndex(projectionColumns[6]))
please note that if you are giving the intent a Uri or location somewhere in external or internal private directory to store the image picture after it gets clicked then you need to broadcast it manually or try to move it to another folder.
here is code to broadcast it by passing the path of the file.
public void broadCastMedia(Context context, String path) {
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File file = new File(path);
intent.setData(Uri.fromFile(file));
context.sendBroadcast(intent);
}
and here is how to copy one to another location say pictures directory then you need to create a temp file to pictures directory and an original file and pass to this.
//temp file
File destFile = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), "image.jpg");
//original file
File sourceFile = new File(uri.getPath());
public static Boolean copyFile(File sourceFile, File destFile)
throws IOException {
if (!destFile.exists()) {
destFile.createNewFile();
FileChannel source = null;
FileChannel destination = null;
try {
source = new FileInputStream(sourceFile).getChannel();
destination = new FileOutputStream(destFile).getChannel();
destination.transferFrom(source, 0, source.size());
} finally {
if (source != null)
source.close();
if (destination != null)
destination.close();
}
return true;
}
return false;
}
and if you have given EXTRA_OUTPUT in intent then you need to store the Uri you have passed as extra as data intent in onActivityResult() will be null at that time you can use the stored Uri to perform any operation on the stored file.

Categories

Resources