-
-
Save ericbrunner/5a4c76cb5cbabc3a992c5a97a216c81c to your computer and use it in GitHub Desktop.
using Acr.UserDialogs; | |
using Plugin.Connectivity; | |
using Plugin.Media; | |
using Plugin.Media.Abstractions; | |
using Plugin.Permissions; | |
using Plugin.Permissions.Abstractions; | |
using System; | |
using System.Globalization; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using trucker_rolsped.AzureStorage; | |
using trucker_rolsped.Helper.HockeyApp; | |
using trucker_rolsped.Interfaces; | |
using trucker_rolsped.Interfaces.Utils; | |
using trucker_rolsped.Models; | |
using trucker_rolsped.StoreManager; | |
using trucker_rolsped.ViewModels.Media; | |
using Xamarin.Forms; | |
namespace trucker_rolsped.Pages.Media | |
{ | |
[Flags] | |
public enum MediaType : byte | |
{ | |
None = 0x00, // 0d, 0000 0000b | |
Photo = 0x01, // 2^0 = 1d, 0000 0001b | |
Video = 0x02, // 2^1 = 2d, 0000 0010b | |
TakenPhoto = 0x04, // 2^2 = 4d, 0000 0100b | |
PickedPhoto = 0x08 // 2^3 = 8d, 0000 1000b | |
} | |
public enum MediaState | |
{ | |
Created = 0, | |
Uploaded = 1, | |
Queued = 2, | |
Error = 3, | |
NotFound = 4 | |
} | |
public sealed class TruckerAppMediaFile : IDisposable | |
{ | |
public MediaType Type { get; set; } | |
public MediaFile File { get; set; } | |
public string DokArt { get; set; } | |
public MediaState MediaState { get; set; } | |
public int TruckAppId { get; set; } | |
public string FilePath { get; set; } | |
public TruckerAppMediaFile() | |
{ | |
MediaState = MediaState.Created; | |
} | |
public async void Dispose() | |
{ | |
File?.Dispose(); | |
} | |
} | |
public partial class Task70PhotoUploadPage : ContentPage, IRefreshPage | |
{ | |
private readonly string _dokArt; | |
private readonly Task70PhotoUploadViewModel _task70PhotoUploadViewModel; | |
private readonly TaskCompletionSource<bool> _tcs; | |
private readonly int _truckAppId; | |
private readonly WorkflowItem _workflowItem; | |
public Task70PhotoUploadPage(TaskCompletionSource<bool> tcs, WorkflowItem workflowItem, string dokArt) | |
{ | |
InitializeComponent(); | |
_tcs = tcs; | |
_workflowItem = workflowItem; | |
_dokArt = dokArt; | |
_truckAppId = _workflowItem.TruckAppId; | |
_task70PhotoUploadViewModel = new Task70PhotoUploadViewModel(_workflowItem, dokArt); | |
BindingContext = _task70PhotoUploadViewModel; | |
NavigationPage.SetHasNavigationBar(this, false); | |
Device.OnPlatform(iOS: () => { Padding = new Thickness(0, 20, 0, 0); }); | |
} | |
protected override bool OnBackButtonPressed() | |
{ | |
return true; | |
} | |
private void ChangeMediaButtonEnableState(bool enable) | |
{ | |
if (TakePhoto.IsVisible) | |
TakePhoto.IsEnabled = enable; | |
if (PickPhoto.IsVisible) | |
PickPhoto.IsEnabled = enable; | |
if (TakeNextPhoto.IsVisible) | |
TakeNextPhoto.IsEnabled = enable; | |
if (PickNextPhoto.IsVisible) | |
PickNextPhoto.IsEnabled = enable; | |
} | |
private async Task CheckToUploadPhoto() | |
{ | |
try | |
{ | |
if (SelectedMedia?.File != null) | |
{ | |
bool isOk = await DisplayAlert("Foto geladen", "Wollen Sie das Foto zur Roland Spedition senden?", "Ja", "Nein"); | |
if (isOk) | |
{ | |
await UploadPhoto(); | |
} | |
} | |
} | |
finally | |
{ | |
SelectedMedia?.Dispose(); | |
} | |
} | |
private async void CloseFotoDialogClicked(object sender, EventArgs e) | |
{ | |
await CheckToUploadPhoto(); | |
await Navigation.PopAsync(animated: false); | |
_tcs.SetResult(true); | |
} | |
// prevent that a user invoked Take and Pick Photo event at same time | |
private readonly SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 1); | |
private async void PickPhotoClicked(object sender, EventArgs e) | |
{ | |
await _semaphoreSlim.WaitAsync(); | |
try | |
{ | |
ChangeMediaButtonEnableState(enable: false); | |
SelectedMedia?.Dispose(); | |
SelectedMedia = null; | |
await CrossMedia.Current.Initialize(); | |
var cameraStatus = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Camera); | |
var storageStatus = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Storage); | |
if (cameraStatus != PermissionStatus.Granted || storageStatus != PermissionStatus.Granted) | |
{ | |
var results = await CrossPermissions.Current.RequestPermissionsAsync(new[] {Permission.Camera, Permission.Storage}); | |
cameraStatus = results[Permission.Camera]; | |
storageStatus = results[Permission.Storage]; | |
} | |
if (cameraStatus == PermissionStatus.Granted && storageStatus == PermissionStatus.Granted) | |
{ | |
if (!CrossMedia.Current.IsPickPhotoSupported) | |
{ | |
await MetricsManagerHelper.Instance.SendGenericMessageToApplicationInsightsAsync($"{nameof(Task70PhotoUploadPage)}", "Not allowed to choose a photo."); | |
await DisplayAlert("Media Info", "Keine Berechtigung ein Foto auszuwählen.", "OK"); | |
return; | |
} | |
SelectedMedia = new TruckerAppMediaFile | |
{ | |
Type = MediaType.Photo | MediaType.PickedPhoto, | |
File = await CrossMedia.Current.PickPhotoAsync(new PickMediaOptions | |
{ | |
CompressionQuality = 92, | |
PhotoSize = PhotoSize.Medium | |
}) | |
}; | |
if (SelectedMedia?.File == null) | |
{ | |
SelectedMedia = null; | |
return; | |
} | |
var imageStream = await DependencyService.Get<Interfaces.Media.IMedia>().CopyMediaAsync(SelectedMedia.File.GetStream()); | |
Image.Source = ImageSource.FromStream(() => imageStream); | |
SendMedia.IsEnabled = true; | |
} | |
else | |
{ | |
await MetricsManagerHelper.Instance.SendGenericMessageToApplicationInsightsAsync($"{nameof(Task70PhotoUploadPage)}", "Not allowed to choose a photo."); | |
await DisplayAlert("Media Info", "Keine Berechtigung ein Foto auszuwählen.", "OK"); | |
if (Device.OS == TargetPlatform.iOS) | |
//On iOS you may want to send your user to the settings screen. | |
CrossPermissions.Current.OpenAppSettings(); | |
} | |
} | |
catch (Exception ex) | |
{ | |
await MetricsManagerHelper.Instance.SendExceptionToApplicationInsightsAsync(ex).ConfigureAwait(true); | |
} | |
finally | |
{ | |
ChangeMediaButtonEnableState(enable: true); | |
_semaphoreSlim.Release(); | |
} | |
} | |
private async void TakePhotoClicked(object sender, EventArgs e) | |
{ | |
await _semaphoreSlim.WaitAsync(); | |
try | |
{ | |
ChangeMediaButtonEnableState(enable: false); | |
SelectedMedia?.Dispose(); | |
SelectedMedia = null; | |
await CrossMedia.Current.Initialize(); | |
var cameraStatus = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Camera); | |
var storageStatus = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Storage); | |
if (cameraStatus != PermissionStatus.Granted || storageStatus != PermissionStatus.Granted) | |
{ | |
var results = await CrossPermissions.Current.RequestPermissionsAsync(new[] {Permission.Camera, Permission.Storage}); | |
cameraStatus = results[Permission.Camera]; | |
storageStatus = results[Permission.Storage]; | |
} | |
if (cameraStatus == PermissionStatus.Granted && storageStatus == PermissionStatus.Granted) | |
{ | |
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported) | |
{ | |
await MetricsManagerHelper.Instance.SendGenericMessageToApplicationInsightsAsync($"{nameof(Task70PhotoUploadPage)}", "No camera available."); | |
await DisplayAlert("Media Info", "Keine Kamera verfügbar.", "OK"); | |
return; | |
} | |
var localFileName = $"{DateTime.Now.ToString("yyyyMMddHHmmss", CultureInfo.InvariantCulture)}_{_truckAppId}_{_dokArt}.jpg"; | |
SelectedMedia = new TruckerAppMediaFile | |
{ | |
Type = MediaType.Photo | MediaType.TakenPhoto, | |
File = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions | |
{ | |
PhotoSize = PhotoSize.Medium, | |
CompressionQuality = 92, | |
Name = localFileName, | |
Directory = "Truckerapp" | |
}) | |
}; | |
if (SelectedMedia?.File == null) | |
{ | |
SelectedMedia = null; | |
return; | |
} | |
var imageStream = await DependencyService.Get<Interfaces.Media.IMedia>().CopyMediaAsync(SelectedMedia.File.GetStream()); | |
Image.Source = ImageSource.FromStream(() => imageStream); | |
SendMedia.IsEnabled = true; | |
} | |
else | |
{ | |
await MetricsManagerHelper.Instance.SendGenericMessageToApplicationInsightsAsync($"{nameof(Task70PhotoUploadPage)}", "Not allowed to use camera."); | |
await DisplayAlert("Media Info", "Keine Berechtigung die Kamera zu verwenden.", "OK"); | |
if (Device.OS == TargetPlatform.iOS) | |
//On iOS you may want to send your user to the settings screen. | |
CrossPermissions.Current.OpenAppSettings(); | |
} | |
} | |
catch (Exception ex) | |
{ | |
await MetricsManagerHelper.Instance.SendExceptionToApplicationInsightsAsync(ex).ConfigureAwait(true); | |
} | |
finally | |
{ | |
ChangeMediaButtonEnableState(enable: true); | |
_semaphoreSlim.Release(); | |
} | |
} | |
private async void SendMediaOnClicked(object sender, EventArgs e) | |
{ | |
SendMedia.IsEnabled = false; | |
try | |
{ | |
await UploadPhoto(); | |
} | |
catch (Exception exception) | |
{ | |
await MetricsManagerHelper.Instance.SendExceptionToApplicationInsightsAsync(exception); | |
Image.Source = Device.OnPlatform( | |
iOS: ImageSource.FromFile("image_photo_plain.png"), | |
Android: ImageSource.FromFile("image_photo_plain.png"), | |
WinPhone: null | |
); | |
SelectedMedia?.Dispose(); | |
SelectedMedia = null; | |
} | |
finally | |
{ | |
EnabledNextButtons(); | |
} | |
} | |
private async Task UploadPhoto() | |
{ | |
try | |
{ | |
SelectedMedia.DokArt = _dokArt; | |
SelectedMedia.TruckAppId = _truckAppId; | |
UserDialogs.Instance.ShowLoading("Foto wird hochgeladen..."); | |
await AzureBlobStorageManager.Instance.UploadMediaAsync(SelectedMedia); | |
UserDialogs.Instance.HideLoading(); | |
switch (SelectedMedia.MediaState) | |
{ | |
case MediaState.Uploaded: | |
//var result = await DisplayAlert("Foto Upload", "Foto wurde erfolgreich hochgeladen. Möchten Sie das Foto jetzt vom Handy löschen?", "Ja", "Nein"); | |
break; | |
case MediaState.Queued: | |
UserDialogs.Instance.ShowLoading("Upload erfolgt automatisch wenn Sie wieder mit Internet oder dem mobilem Netz verbunden sind!"); | |
await Task.Delay(1500); | |
UserDialogs.Instance.HideLoading(); | |
break; | |
case MediaState.Created: | |
await DisplayAlert("Foto Upload", "Sie müssen das Foto erneut auswählen bzw. machen, da es nicht gesendet wurde!", "Ok"); | |
break; | |
case MediaState.Error: | |
await DisplayAlert("Foto Upload", "Sie müssen das Foto erneut auswählen bzw. machen, da es nicht gesendet wurde!", "Ok"); | |
break; | |
default: | |
await DisplayAlert("Foto Upload", "Sie müssen das Foto erneut auswählen bzw. machen, da es nicht gesendet wurde!", "Ok"); | |
break; | |
} | |
} | |
catch (Exception ex) | |
{ | |
SelectedMedia.MediaState = MediaState.Error; | |
await DisplayAlert("Sende Media Fehler", ex.Message, "Ok"); | |
} | |
finally | |
{ | |
Image.Source = Device.OnPlatform( | |
iOS: ImageSource.FromFile("image_photo_plain.png"), | |
Android: ImageSource.FromFile("image_photo_plain.png"), | |
WinPhone: null | |
); | |
if (SelectedMedia.MediaState == MediaState.Uploaded || SelectedMedia.MediaState == MediaState.Queued) | |
{ | |
_workflowItem.TruckAuftragWorkFlow.DokAnzahl++; | |
} | |
SelectedMedia?.Dispose(); | |
if (SelectedMedia?.MediaState == MediaState.Uploaded) | |
{ | |
await DeleteFileAfterUploadAsync(); | |
} | |
SelectedMedia = null; | |
} | |
} | |
private async Task DeleteFileAfterUploadAsync() | |
{ | |
//Platform specific file delete | |
var truckerappMedia = SelectedMedia.FilePath; | |
var platformFileHandler = DependencyService.Get<IFileHandling>(); | |
if (platformFileHandler != null) | |
if (await platformFileHandler.FileExistsAsync(truckerappMedia)) | |
{ | |
var deleted = await platformFileHandler.DeleteFileAsync(truckerappMedia); | |
if (!deleted) | |
{ | |
await MetricsManagerHelper.Instance.SendErrorToApplicationInsightsAsync($"Photo couldn't be deleted: {truckerappMedia}"); | |
} | |
} | |
} | |
private void EnabledNextButtons() | |
{ | |
TakePhoto.IsVisible = false; | |
PickPhoto.IsVisible = false; | |
TakeNextPhoto.IsVisible = true; | |
PickNextPhoto.IsVisible = true; | |
} | |
private bool IsTakenPhoto => (SelectedMedia.Type & MediaType.TakenPhoto) != 0x00; | |
private TruckerAppMediaFile SelectedMedia { get; set; } | |
protected override async void OnAppearing() | |
{ | |
base.OnAppearing(); | |
try | |
{ | |
await RefreshItemsAsync(showActivityIndicator: false, syncItems: false); | |
} | |
catch (Exception e) | |
{ | |
await MetricsManagerHelper.Instance.SendExceptionToApplicationInsightsAsync(e); | |
} | |
} | |
public async Task RefreshItemsAsync(bool showActivityIndicator, bool syncItems) | |
{ | |
syncItems = syncItems && CrossConnectivity.Current.IsConnected; | |
//UserDialogs.Instance.ShowLoading("Daten werden geladen...", MaskType.Black); | |
_task70PhotoUploadViewModel.LanguageItems = await OfflineSyncStoreManager.Instance.TagSpracheStore.GetTagSpracheItemsAsync(syncItems); | |
//UserDialogs.Instance.HideLoading(); | |
} | |
} | |
} |
@Ines-Diaz Well to be honest, that is very long ago when I posted that gist ;-) I can only suggest that you use the official Xamarin.Essentials MediaPicker and if you have any questions post it in the GitHub Repo:
Documentation: https://docs.microsoft.com/en-us/xamarin/essentials/media-picker?context=xamarin%2Fandroid&tabs=android
GitHub Repo: https://github.com/xamarin/Essentials
MediaPicker Source: https://github.com/xamarin/Essentials/tree/main/Xamarin.Essentials/MediaPicker
I added that to suppress that concurrency error as stated // prevent that a user invoked Take and Pick Photo event at same time
You currently have a delay of a few seconds as far as I understand between taking the image on the button page and displaying the image on another page.
Do you use that UploadPhoto method?: There is a delay: https://gist.github.com/ericbrunner/5a4c76cb5cbabc3a992c5a97a216c81c#file-task70photouploadpage-xaml-cs-L333
Well that is my custom code so feel free to grap and modify it your needs, but I could only give you the advice to go with the official Xamarin.Essentials , clone the git repo and start the samples and take a look in the usage of the MediaPicker here: https://github.com/xamarin/Essentials/blob/main/Samples/Samples/ViewModel/MediaPickerViewModel.cs
@ericbrunner Thank you very much for your response. I'll put into practice. All the best :)
Also, I would like to tell you about another problem. After taking the photo and navigating to the next page, the camera automatically reopens and I can't figure out the error. Thank you very much, best regards.