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

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 :)

Related

Xamarin Forms Android 10 and higher find video by filename in public external storage and get path

im developing a Xamarin Forms App where the User can take Videos and save them to the public external storage. Im currently saving the filename of the created video.
My way of saving the video:
private readonly string DirectoryName = "KiloFürKilo";
public async Task<string> CaptureVideoAsync()
{
var photo = await MediaPicker.CaptureVideoAsync();
await using var stream = await photo.OpenReadAsync();
await using var memoryStream = new MemoryStream();
await stream.CopyToAsync(memoryStream);
var filename = "KforK" + DateTime.Now + ".mp4";
SaveVideoFromByte(memoryStream.ToArray(), filename);
return filename;
}
private async void SaveVideoFromByte(byte[] imageByte, string filename)
{
var context = CrossCurrentActivity.Current.AppContext;
var mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
//Android 10+
if (Android.OS.Build.VERSION.SdkInt > Android.OS.BuildVersionCodes.P)
{
using var resolver = context.ContentResolver;
var contentValues = new ContentValues();
contentValues.Put(MediaStore.IMediaColumns.DisplayName, filename);
contentValues.Put(MediaStore.IMediaColumns.MimeType, "video/mp4");
contentValues.Put(MediaStore.IMediaColumns.RelativePath, "DCIM/" + DirectoryName);
var uri = resolver.Insert(MediaStore.Video.Media.ExternalContentUri, contentValues);
using var stream = resolver.OpenOutputStream(uri);
await stream.WriteAsync(imageByte);
stream.Close();
mediaScanIntent.SetData(uri);
}
else
{
var rootPath =
Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryMovies);
var storagePath = Path.Combine(rootPath.ToString(), DirectoryName);
if (!File.Exists(storagePath))
{
Directory.CreateDirectory(storagePath);
}
string path = Path.Combine(storagePath.ToString(), filename);
File.WriteAllBytes(path, imageByte);
mediaScanIntent.SetData(Android.Net.Uri.FromFile(new Java.IO.File(path)));
}
context.SendBroadcast(mediaScanIntent);
}
I now want to be able to open and play the video, without the user need to pick it from the gallery.
How can i find a video in the external public storage by a filename and retrieve the path to it?
I think i have to use the MediaStore but could not figure out how.
Thanks to anyone who can help me with this problem:)
MediaStore insert() gave you an uri where you wrote the file.
To read/play the file you can use the same uri.
Further your relative path was "DCIM/"+DirectoryName;
And if you need a path then complete path is
"/storage/emulated/0/DCIM/"+DirectoryName+"/"+filename.

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!

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.

Android - Why my saved image is not appearing in the default gallery of my phone?

I am trying to save an image from my application to the default gallery of my phone. The code below works perfectly if I have a SD card on the phone. The image saved appears in the phone's gallery and everything, as expected:
private Uri saveMediaEntry(File f, String title, String description, int orientation, Location loc) {
ContentValues v = new ContentValues();
v.put(Images.Media.TITLE, title);
v.put(Images.Media.DISPLAY_NAME, title);
v.put(Images.Media.DESCRIPTION, description);
v.put(Images.Media.ORIENTATION, orientation);
String nameFile = f.getName();
File parent = f.getParentFile() ;
String path = parent.toString().toLowerCase() ;
String nameParent = parent.getName().toLowerCase() ;
v.put(Images.ImageColumns.BUCKET_ID, path.hashCode());
v.put(Images.ImageColumns.BUCKET_DISPLAY_NAME, nameParent);
v.put(Images.Media.SIZE,f.length()) ;
if( nameFile.toLowerCase().contains(".png") ){
v.put(Images.Media.MIME_TYPE, "image/png");
}else if( nameFile.toLowerCase().contains(".jpg") ||
nameFile.toLowerCase().contains(".jpeg") ){
v.put(Images.Media.MIME_TYPE, "image/jpeg");
}else{
v.put(Images.Media.MIME_TYPE, "image/jpeg");
}
String imagePath = f.getAbsolutePath();
v.put("_data", imagePath) ;
ContentResolver c = getContentResolver() ;
Uri uriOfSucessfulySavedImage = null;
uriOfSucessfulySavedImage = c.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, v);
return uriOfSucessfulySavedImage;
}
However, if I try to save the same image into the internal storage (for when the phone does not have a SD card), the image does not appear in the phone's gallery! To try to do that, I only change one line from the above code:
uriOfSucessfulySavedImage = c.insert(MediaStore.Images.Media.INTERNAL_CONTENT_URI, v);
The interesting thing about this, however, is that the variable uriOfSucessfulySavedImage is not null (it returns content://media/internal/images/media/x, where 'x' is a number). So, the image is being saved somewhere in the internal storage of the phone, but it is not getting displayed in the phone gallery's as when I use MediaStore.Images.Media.EXTERNAL_CONTENT_URI.
Does anybody have any clue what is going on? How can I save an image into the internal storage of the phone and have that image in the phone's gallery?
Update
I forgot one important information. The File "f" in the parameters of the method "saveMediaEntry" is coming from this other method for when the SD card is mounted (that is, for the first code):
public static File getCacheDirectory(String desiredNameOfTheDirectory){
File fileCacheDir = null;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) ){
fileCacheDir = new File( Environment.getExternalStorageDirectory(), desiredNameOfTheDirectory );
}
if(!fileCacheDir.exists()){
fileCacheDir.mkdirs();
}
return fileCacheDir;
}
and from the following code for when the SD card is not founded:
public static File getCacheDirectory(String desiredNameOfTheDirectory, Context context){
File fileCacheDir = null;
fileCacheDir = context.getCacheDir();
if(!fileCacheDir.exists()){
fileCacheDir.mkdirs();
}
return fileCacheDir;
}
Another easy way to do it. Add this after saving your file.
File imageFile = ...
MediaScannerConnection.scanFile(this, new String[] { imageFile.getPath() }, new String[] { "image/jpeg" }, null);
I haven't tried this, but I believe you need to run the Media Scanner to scan the internal storage directory so that the gallery can see your newly saved image. Check this post here.
Copy Past this Function in your Activity
private void scanner(String path) {
MediaScannerConnection.scanFile(FrameActivity.this,
new String[] { path }, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.i("TAG", "Finished scanning " + path);
}
});
}
And then add this Line where you save your image
scanner(imageFile.getAbsolutePath());
Try this.
Write down this line once image stored in gallery.
File file = ..... // Save file
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file)));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
Uri.parse("file://"
+ Environment.getExternalStorageDirectory())));
} else {
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse("file://"
+ Environment.getExternalStorageDirectory())));
}

Create a custom album in the Android Gallery App

I want create a new 'album' in Android. I unserstood that you have to do it with your own ContentProvider. But i can't figure out how. If you take pictures, all the images appear in the album 'Camera'. If you have installed Google+ you'll see 2 extra albums: Instant Upload & Profile Picture.
We want to create something similar. We have an album per user online and we want that it appears as an album item in the Gallery app. Can someone point me in the right direction.
I already checked: http://developer.android.com/guide/topics/providers/content-providers.html
Actually im not really sure if it is possible.
You can use this way to create album in Gallery app. The name appears as 'app images'.
String path = Environment.getExternalStorageDirectory().toString();
File dir = new File(path, "/appname/media/app images/");
if (!dir.isDirectory()) {
dir.mkdirs();
}
File file = new File(dir, filename + ".jpg");
String imagePath = file.getAbsolutePath();
//scan the image so show up in album
MediaScannerConnection.scanFile(this,
new String[] { imagePath }, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
if(Config.LOG_DEBUG_ENABLED) {
Log.d(Config.LOGTAG, "scanned : " + path);
}
}
});

Categories

Resources