Skip to content

Commit

Permalink
Fix Android MediaPicker Photo capture logic (#19844)
Browse files Browse the repository at this point in the history
* Use result stream for photo/video source in sample

* Use old capture photo logic

In #18620 we changed the photo capture logic along with the video.  The photo logic should have stayed how it was, this splits things out a bit so that the two paths are a bit more obviously separate, and reverts back to the older photo capture logic.
  • Loading branch information
Redth committed Jan 15, 2024
1 parent 7ae0222 commit 87f59a8
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 59 deletions.
4 changes: 2 additions & 2 deletions src/Essentials/samples/Samples/View/MediaPickerPage.xaml
Expand Up @@ -18,8 +18,8 @@
<Button Text="Pick video" Command="{Binding PickVideoCommand}" />
<Button Text="Capture video" Command="{Binding CaptureVideoCommand}" />

<Image Source="{Binding PhotoPath}" IsVisible="{Binding ShowPhoto}" HeightRequest="300"/>
<!--<MediaElement VerticalOptions="FillAndExpand" Source="{Binding VideoPath}" IsVisible="{Binding ShowVideo}" />-->
<Image Source="{Binding PhotoSource}" IsVisible="{Binding ShowPhoto}" HeightRequest="300"/>
<!--<MediaElement VerticalOptions="FillAndExpand" Source="{Binding VideoSource}" IsVisible="{Binding ShowVideo}" />-->
</StackLayout>
</ScrollView>
</Grid>
Expand Down
52 changes: 20 additions & 32 deletions src/Essentials/samples/Samples/ViewModel/MediaPickerViewModel.cs
Expand Up @@ -10,8 +10,8 @@ namespace Samples.ViewModel
{
public class MediaPickerViewModel : BaseViewModel
{
string photoPath;
string videoPath;
ImageSource photoSource;
ImageSource videoSource;

bool showPhoto;
bool showVideo;
Expand Down Expand Up @@ -45,16 +45,16 @@ public bool ShowVideo
set => SetProperty(ref showVideo, value);
}

public string PhotoPath
public ImageSource PhotoSource
{
get => photoPath;
set => SetProperty(ref photoPath, value);
get => photoSource;
set => SetProperty(ref photoSource, value);
}

public string VideoPath
public ImageSource VideoSource
{
get => videoPath;
set => SetProperty(ref videoPath, value);
get => videoSource;
set => SetProperty(ref videoSource, value);
}

async void DoPickPhoto()
Expand All @@ -65,7 +65,7 @@ async void DoPickPhoto()

await LoadPhotoAsync(photo);

Console.WriteLine($"PickPhotoAsync COMPLETED: {PhotoPath}");
Console.WriteLine($"PickPhotoAsync COMPLETED: {PhotoSource}");
}
catch (Exception ex)
{
Expand All @@ -81,7 +81,7 @@ async void DoCapturePhoto()

await LoadPhotoAsync(photo);

Console.WriteLine($"CapturePhotoAsync COMPLETED: {PhotoPath}");
Console.WriteLine($"CapturePhotoAsync COMPLETED: {PhotoSource}");
}
catch (Exception ex)
{
Expand All @@ -97,7 +97,7 @@ async void DoPickVideo()

await LoadVideoAsync(video);

Console.WriteLine($"PickVideoAsync COMPLETED: {PhotoPath}");
Console.WriteLine($"PickVideoAsync COMPLETED: {PhotoSource}");
}
catch (Exception ex)
{
Expand All @@ -113,7 +113,7 @@ async void DoCaptureVideo()

await LoadVideoAsync(photo);

Console.WriteLine($"CaptureVideoAsync COMPLETED: {VideoPath}");
Console.WriteLine($"CaptureVideoAsync COMPLETED: {VideoSource}");
}
catch (Exception ex)
{
Expand All @@ -126,19 +126,13 @@ async Task LoadPhotoAsync(FileResult photo)
// canceled
if (photo == null)
{
PhotoPath = null;
PhotoSource = null;
return;
}

// save the file into local storage
var newFile = Path.Combine(FileSystem.CacheDirectory, photo.FileName);
using (var stream = await photo.OpenReadAsync())
using (var newStream = File.OpenWrite(newFile))
{
await stream.CopyToAsync(newStream);
}
var stream = await photo.OpenReadAsync();
PhotoSource = ImageSource.FromStream(() => stream);

PhotoPath = newFile;
ShowVideo = false;
ShowPhoto = true;
}
Expand All @@ -148,27 +142,21 @@ async Task LoadVideoAsync(FileResult video)
// canceled
if (video == null)
{
VideoPath = null;
VideoSource = null;
return;
}

// save the file into local storage
var newFile = Path.Combine(FileSystem.CacheDirectory, video.FileName);
using (var stream = await video.OpenReadAsync())
using (var newStream = File.OpenWrite(newFile))
{
await stream.CopyToAsync(newStream);
}
var stream = await video.OpenReadAsync();
VideoSource = ImageSource.FromStream(() => stream);

VideoPath = newFile;
ShowVideo = true;
ShowPhoto = false;
}

public override void OnDisappearing()
{
PhotoPath = null;
VideoPath = null;
PhotoSource = null;
VideoSource = null;

base.OnDisappearing();
}
Expand Down
78 changes: 53 additions & 25 deletions src/Essentials/src/MediaPicker/MediaPicker.android.cs
Expand Up @@ -66,46 +66,74 @@ public async Task<FileResult> CaptureAsync(MediaPickerOptions options, bool phot
if (!OperatingSystem.IsAndroidVersionAtLeast(33))
await Permissions.EnsureGrantedAsync<Permissions.StorageWrite>();

var capturePhotoIntent = new Intent(photo ? MediaStore.ActionImageCapture : MediaStore.ActionVideoCapture);
var captureIntent = new Intent(photo ? MediaStore.ActionImageCapture : MediaStore.ActionVideoCapture);

if (!PlatformUtils.IsIntentSupported(capturePhotoIntent))
throw new FeatureNotSupportedException($"Either there was no camera on the device or '{capturePhotoIntent.Action}' was not added to the <queries> element in the app's manifest file. See more: https://developer.android.com/about/versions/11/privacy/package-visibility");
if (!PlatformUtils.IsIntentSupported(captureIntent))
throw new FeatureNotSupportedException($"Either there was no camera on the device or '{captureIntent.Action}' was not added to the <queries> element in the app's manifest file. See more: https://developer.android.com/about/versions/11/privacy/package-visibility");

capturePhotoIntent.AddFlags(ActivityFlags.GrantReadUriPermission);
capturePhotoIntent.AddFlags(ActivityFlags.GrantWriteUriPermission);
captureIntent.AddFlags(ActivityFlags.GrantReadUriPermission);
captureIntent.AddFlags(ActivityFlags.GrantWriteUriPermission);

try
{
var activity = ActivityStateManager.Default.GetCurrentActivity(true);

// Create the temporary file
var ext = photo
? FileExtensions.Jpg
: FileExtensions.Mp4;
var fileName = Guid.NewGuid().ToString("N") + ext;
var tmpFile = FileSystemUtils.GetTemporaryFile(Application.Context.CacheDir, fileName);
string captureResult = null;

string path = null;
if (photo)
captureResult = await CapturePhotoAsync(captureIntent);
else
captureResult = await CaptureVideoAsync(captureIntent);

// Return the file that we just captured
return new FileResult(captureResult);
}
catch (OperationCanceledException)
{
return null;
}
}

void OnResult(Intent intent)
{
// The uri returned is only temporary and only lives as long as the Activity that requested it,
// so this means that it will always be cleaned up by the time we need it because we are using
// an intermediate activity.
async Task<string> CapturePhotoAsync(Intent captureIntent)
{
// Create the temporary file
var fileName = Guid.NewGuid().ToString("N") + FileExtensions.Jpg;
var tmpFile = FileSystemUtils.GetTemporaryFile(Application.Context.CacheDir, fileName);

path = FileSystemUtils.EnsurePhysicalPath(intent.Data);
}
// Set up the content:// uri
AndroidUri outputUri = null;

// Start the capture process
await IntermediateActivity.StartAsync(capturePhotoIntent, PlatformUtils.requestCodeMediaCapture, onResult: OnResult);
void OnCreate(Intent intent)
{
// Android requires that using a file provider to get a content:// uri for a file to be called
// from within the context of the actual activity which may share that uri with another intent
// it launches.
outputUri ??= FileProvider.GetUriForFile(tmpFile);

// Return the file that we just captured
return new FileResult(path);
intent.PutExtra(MediaStore.ExtraOutput, outputUri);
}
catch (OperationCanceledException)

await IntermediateActivity.StartAsync(captureIntent, PlatformUtils.requestCodeMediaCapture, OnCreate);

return tmpFile.AbsolutePath;
}

async Task<string> CaptureVideoAsync(Intent captureIntent)
{
string path = null;

void OnResult(Intent intent)
{
return null;
// The uri returned is only temporary and only lives as long as the Activity that requested it,
// so this means that it will always be cleaned up by the time we need it because we are using
// an intermediate activity.
path = FileSystemUtils.EnsurePhysicalPath(intent.Data);
}

// Start the capture process
await IntermediateActivity.StartAsync(captureIntent, PlatformUtils.requestCodeMediaCapture, onResult: OnResult);

return path;
}
}
}

0 comments on commit 87f59a8

Please sign in to comment.