Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue34: Edit applicant photo in mobile app #41

Merged
merged 6 commits into from
Sep 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions Mobile/Extensions/MauiExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Carmen.Mobile.Extensions
{
internal static class MauiExtensions
{
/// <summary>This is primarily required because ImageButton doesn't work.
/// It completely ignores the Aspect setting and displays like junk, it is unusable.
/// This extension allows you to add a Tapped event handler to a non-tappable Image, which DOES work.</summary>
public static void AddTapHandler(this View view, EventHandler<TappedEventArgs> handler, int number_of_taps_required = 1)
{
var tapped = new TapGestureRecognizer
{
NumberOfTapsRequired = number_of_taps_required
};
tapped.Tapped += handler;
view.GestureRecognizers.Add(tapped);
}
}
}
6 changes: 5 additions & 1 deletion Mobile/Platforms/Android/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<queries>
<intent>
<action android:name="android.media.action.IMAGE_CAPTURE" />
</intent>
</queries>
</manifest>
8 changes: 8 additions & 0 deletions Mobile/Platforms/Android/MainApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@

[assembly: UsesPermission(Android.Manifest.Permission.Internet)]

// Needed for Picking photo
[assembly: UsesPermission(Android.Manifest.Permission.ReadExternalStorage, MaxSdkVersion = 32)]
[assembly: UsesPermission(Android.Manifest.Permission.ReadMediaImages)]

// Needed for Taking photo
[assembly: UsesPermission(Android.Manifest.Permission.Camera)]
[assembly: UsesPermission(Android.Manifest.Permission.WriteExternalStorage, MaxSdkVersion = 32)]

namespace Carmen.Mobile
{
[Application]
Expand Down
8 changes: 8 additions & 0 deletions Mobile/Platforms/MacCatalyst/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,13 @@
<string>David Lang © 2023</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>NSCameraUsageDescription</key>
<string>This app needs access to the camera to take photos.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs access to microphone for taking videos.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>This app needs access to the photo gallery for picking photos and videos.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs access to photos gallery for picking photos and videos.</string>
</dict>
</plist>
8 changes: 8 additions & 0 deletions Mobile/Platforms/iOS/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,13 @@
<string>Assets.xcassets/appicon.appiconset</string>
<key>UILaunchStoryboardName</key>
<string>MauiSplash</string>
<key>NSCameraUsageDescription</key>
<string>This app needs access to the camera to take photos.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs access to microphone for taking videos.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>This app needs access to the photo gallery for picking photos and videos.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs access to photos gallery for picking photos and videos.</string>
</dict>
</plist>
33 changes: 33 additions & 0 deletions Mobile/Popups/ListPopup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using CommunityToolkit.Maui.Views;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Carmen.Mobile.Popups
{
internal class ListPopup<T> : Popup
{
public ListPopup(T[] items, Func<T, string> display_getter)
{
var layout = new VerticalStackLayout
{
Spacing = 5
};
foreach (var item in items)
{
var button = new Button
{
Text = display_getter(item)
};
button.Clicked += (s, e) =>
{
Close(item);
};
layout.Children.Add(button);
}
Content = layout;
}
}
}
Binary file added Mobile/Resources/Images/no_photo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 50 additions & 1 deletion Mobile/Views/ApplicantDetails.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
using Carmen.Desktop.Converters;
using Carmen.Mobile.Converters;
using Carmen.Mobile.Extensions;
using Carmen.Mobile.Models;
using Carmen.Mobile.Popups;
using Carmen.ShowModel;
using Carmen.ShowModel.Applicants;
using Carmen.ShowModel.Criterias;
using Carmen.ShowModel.Structure;
using CommunityToolkit.Maui.Views;
using Serilog;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
Expand Down Expand Up @@ -125,7 +129,7 @@ private async void ViewApplicant_Loaded(object? sender, EventArgs e)
else if (await Task.Run(() => applicant.Photo) is SM.Image image)
source = await ActualImage(image);
else
source = null;
source = ImageSource.FromFile("no_photo.png");
model.LoadedPhoto(source);
}

Expand Down Expand Up @@ -246,13 +250,58 @@ private View GenerateSideView()
activity.SetBinding(ActivityIndicator.IsVisibleProperty, new Binding(nameof(ApplicantModel.IsLoadingPhoto)));
var image = new MC.Image();
image.SetBinding(MC.Image.SourceProperty, new Binding(nameof(ApplicantModel.Photo)));
image.AddTapHandler(Image_Clicked);
return new Grid
{
image,
activity
};
}

private async void Image_Clicked(object? sender, EventArgs e)
{
if (model.Applicant == null)
return;
// choose how we get the image
Func<MediaPickerOptions?, Task<FileResult?>> getter;
if (MediaPicker.Default.IsCaptureSupported)
{
var options = new[]
{
"Take a photo",
"Pick an existing photo"
};
var popup = new ListPopup<string>(options, s => s);
var result = await this.ShowPopupAsync(popup);
if (result == options[0])
getter = MediaPicker.Default.CapturePhotoAsync;
else if (result == options[1])
getter = MediaPicker.Default.PickPhotoAsync;
else
return;
}
else
{
getter = MediaPicker.Default.PickPhotoAsync;
}
// actually get the image
if (await getter(null) is FileResult file)
{
var photo = new SM.Image
{
Name = file.FileName
};
using Stream source_stream = await file.OpenReadAsync();
using (var memory_stream = new MemoryStream())
{
source_stream.CopyTo(memory_stream);
photo.ImageData = memory_stream.ToArray();
}
model.Applicant.Photo = photo;
model.LoadedPhoto(await ActualImage(photo));
}
}

private async void Save_Clicked(object? sender, EventArgs e)
{
if (context == null)
Expand Down