Incident Report Layout Update

main
unknown 6 months ago
parent 22ee251227
commit 28afc9a2a5
  1. 33
      Helpers/EmailHelper.cs
  2. 78
      Helpers/EmailService.cs
  3. 34
      Helpers/GeolocationHelper.cs
  4. 2
      Helpers/SosService.cs
  5. 4
      Justice.csproj.user
  6. 2
      Models/IncidentReport.cs
  7. 2
      Views/DashboardPage.xaml.cs
  8. 47
      Views/IncidentReportPage.xaml
  9. 114
      Views/IncidentReportPage.xaml.cs
  10. BIN
      bin/Debug/net9.0-android/Justice.dll
  11. BIN
      bin/Debug/net9.0-android/Justice.pdb
  12. 22
      bin/Debug/net9.0-android/Justice.xml
  13. BIN
      bin/Debug/net9.0-android/com.companyname.justice-Signed.apk
  14. BIN
      bin/Debug/net9.0-android/com.companyname.justice-Signed.apk.idsig
  15. BIN
      bin/Debug/net9.0-android/com.companyname.justice.apk
  16. BIN
      bin/Debug/net9.0-ios/iossimulator-x64/BouncyCastle.Cryptography.dll
  17. BIN
      bin/Debug/net9.0-ios/iossimulator-x64/Justice.dll
  18. BIN
      bin/Debug/net9.0-ios/iossimulator-x64/Justice.pdb
  19. BIN
      bin/Debug/net9.0-ios/iossimulator-x64/MailKit.dll
  20. BIN
      bin/Debug/net9.0-ios/iossimulator-x64/MimeKit.dll
  21. BIN
      bin/Debug/net9.0-ios/iossimulator-x64/System.Security.Cryptography.Pkcs.dll
  22. BIN
      bin/Debug/net9.0-maccatalyst/maccatalyst-x64/BouncyCastle.Cryptography.dll
  23. BIN
      bin/Debug/net9.0-maccatalyst/maccatalyst-x64/Justice.dll
  24. BIN
      bin/Debug/net9.0-maccatalyst/maccatalyst-x64/Justice.pdb
  25. BIN
      bin/Debug/net9.0-maccatalyst/maccatalyst-x64/MailKit.dll
  26. BIN
      bin/Debug/net9.0-maccatalyst/maccatalyst-x64/MimeKit.dll
  27. BIN
      bin/Debug/net9.0-maccatalyst/maccatalyst-x64/System.Security.Cryptography.Pkcs.dll
  28. BIN
      bin/Debug/net9.0-windows10.0.19041.0/win10-x64/BouncyCastle.Cryptography.dll
  29. 78
      bin/Debug/net9.0-windows10.0.19041.0/win10-x64/Justice.deps.json
  30. BIN
      bin/Debug/net9.0-windows10.0.19041.0/win10-x64/Justice.dll
  31. BIN
      bin/Debug/net9.0-windows10.0.19041.0/win10-x64/Justice.exe
  32. BIN
      bin/Debug/net9.0-windows10.0.19041.0/win10-x64/Justice.pdb
  33. BIN
      bin/Debug/net9.0-windows10.0.19041.0/win10-x64/MailKit.dll
  34. BIN
      bin/Debug/net9.0-windows10.0.19041.0/win10-x64/MimeKit.dll
  35. BIN
      bin/Debug/net9.0-windows10.0.19041.0/win10-x64/System.Security.Cryptography.Pkcs.dll

@ -0,0 +1,33 @@
using Microsoft.Maui.ApplicationModel.Communication;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Justice.Helpers
{
public static class EmailHelper
{
public static async Task SendEmailAsync(string subject, string body, string recipient)
{
try
{
var message = new EmailMessage
{
Subject = subject,
Body = body,
To = new List<string> { recipient }
};
await Email.ComposeAsync(message);
}
catch (FeatureNotSupportedException)
{
throw new Exception("Email is not supported on this device or platform.");
}
catch (Exception ex)
{
throw new Exception($"Unable to send email: {ex.Message}");
}
}
}
}

@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Mail;
using System.Threading.Tasks;
namespace ChildGuard.Services
{
public class EmailService
{
private readonly string _smtpServer = "smtp.gmail.com";
private readonly int _smtpPort = 587;
private readonly string _senderEmail = "amritsyangtan1@gmail.com"; // Replace with your email
private readonly string _senderPassword = "bdcx ycpy cbrr szpm"; // Replace with your app password
private readonly Dictionary<string, string> _incidentTypeToEmail;
public EmailService()
{
// Map incident types to authority emails
_incidentTypeToEmail = new Dictionary<string, string>
{
{ "Accident", "amreitsyanf@gmail.com" },
{ "Crime", "crime-authority@example.com" },
{ "Fire", "fire-department@example.com" },
{ "Other", "general-authority@example.com" }
};
}
/// <summary>
/// Sends an email using SMTP.
/// </summary>
/// <param name="recipientEmail">Recipient's email address.</param>
/// <param name="subject">Email subject.</param>
/// <param name="body">Email body.</param>
/// <returns>True if successful, otherwise false.</returns>
public async Task<bool> SendEmailAsync(string recipientEmail, string subject, string body)
{
try
{
using (var smtpClient = new SmtpClient(_smtpServer, _smtpPort))
{
smtpClient.Credentials = new NetworkCredential(_senderEmail, _senderPassword);
smtpClient.EnableSsl = true;
var mailMessage = new MailMessage
{
From = new MailAddress(_senderEmail),
Subject = subject,
Body = body,
IsBodyHtml = true
};
mailMessage.To.Add(recipientEmail);
await smtpClient.SendMailAsync(mailMessage);
}
Console.WriteLine("Email sent successfully.");
return true;
}
catch (Exception ex)
{
Console.WriteLine($"Error sending email: {ex.Message}");
return false;
}
}
/// <summary>
/// Gets the recipient email based on the incident type.
/// </summary>
/// <param name="incidentType">Incident type.</param>
/// <returns>Recipient email address.</returns>
public string GetRecipientEmail(string incidentType)
{
return _incidentTypeToEmail.TryGetValue(incidentType, out var email) ? email : "default-authority@example.com";
}
}
}

@ -1,22 +1,20 @@
using System;
using System.Collections.Generic;
using Microsoft.Maui.ApplicationModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Maui.ApplicationModel;
namespace Justice.Helpers
{
public class GeolocationHelper
public static class GeolocationHelper
{
/// <summary>
/// Retrieves the user's current latitude, longitude, and a readable address.
/// Fetches the user's current latitude, longitude, and readable address.
/// </summary>
/// <returns>A tuple containing Latitude, Longitude, and Address.</returns>
public static async Task<(double Latitude, double Longitude, string Address)> GetCurrentLocationAsync()
public static async Task<(double Latitude, double Longitude, string Address)> GetLocationAsync()
{
try
{
// Request the user's current location
var location = await Geolocation.Default.GetLocationAsync(new GeolocationRequest
{
DesiredAccuracy = GeolocationAccuracy.High,
@ -25,36 +23,30 @@ namespace Justice.Helpers
if (location != null)
{
// Reverse geocode the location to get the address
var placemarks = await Geocoding.Default.GetPlacemarksAsync(location);
var placemark = placemarks.FirstOrDefault();
string address = "Address not found";
if (placemark != null)
{
address = $"{placemark.SubLocality}, {placemark.Thoroughfare}, " +
$"{placemark.Locality}, {placemark.AdminArea}, {placemark.CountryName}";
}
string address = placemark != null
? $"{placemark.Thoroughfare}, {placemark.Locality}, {placemark.AdminArea}, {placemark.CountryName}"
: "Address not found";
return (location.Latitude, location.Longitude, address);
}
// Return a default value if location is null
return (0, 0, "Unable to fetch location.");
throw new Exception("Location not available.");
}
catch (FeatureNotSupportedException)
{
// Return a default value if location services are not supported
return (0, 0, "Location services are not supported on this device.");
throw new Exception("Location services are not supported on this device.");
}
catch (PermissionException)
{
// Return a default value if permissions are not granted
return (0, 0, "Location permissions are not granted.");
throw new Exception("Location permissions are not granted.");
}
catch (Exception ex)
{
// Return a default value for other errors
return (0, 0, $"An error occurred: {ex.Message}");
throw new Exception($"Unable to fetch location: {ex.Message}");
}
}
}

@ -12,7 +12,7 @@ namespace Justice.Services
try
{
// Fetch location and address
(double latitude, double longitude, string address) = await GeolocationHelper.GetCurrentLocationAsync();
(double latitude, double longitude, string address) = await GeolocationHelper.GetLocationAsync();
// Prepare the SOS message

@ -3,8 +3,8 @@
<PropertyGroup>
<IsFirstTimeProjectOpen>False</IsFirstTimeProjectOpen>
<ActiveDebugFramework>net9.0-android</ActiveDebugFramework>
<ActiveDebugProfile>Samsung SM-M325F (Android 13.0 - API 33)</ActiveDebugProfile>
<SelectedPlatformGroup>PhysicalDevice</SelectedPlatformGroup>
<ActiveDebugProfile>Medium Phone API 35 (Android 15.0 - API 35)</ActiveDebugProfile>
<SelectedPlatformGroup>Emulator</SelectedPlatformGroup>
<DefaultDevice>Medium_Phone_API_35</DefaultDevice>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net9.0-android|AnyCPU'">

@ -11,7 +11,7 @@ namespace Justice.Models
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string ReportName { get; set; }
public string ReporterName { get; set; }
public string IncidentType { get; set; }
public string Description { get; set; }
public double Latitude { get; set; }

@ -27,7 +27,7 @@ public partial class DashboardPage : ContentPage
}
private void OnIncidentReportClicked(object sender, EventArgs e)
{
DisplayAlert("Report Incident button clicked", "Reprot", "OK");
Navigation.PushAsync(new IncidentReportPage());
}
private void OnSOSButtonClicked(object sender, EventArgs e)

@ -1,12 +1,41 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Justice.Views.IncidentReportPage"
Title="IncidentReportPage">
<VerticalStackLayout>
<Label
Text="Welcome to .NET MAUI!"
VerticalOptions="Center"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ContentPage>
Title="Report Incident">
<ScrollView>
<StackLayout Padding="20" Spacing="15">
<!-- Reporter Name -->
<Label Text="Your Name" FontSize="Medium" />
<Entry x:Name="ReporterNameEntry" Placeholder="Enter your name" />
<!-- Incident Type -->
<Label Text="Incident Type" FontSize="Medium" />
<Picker x:Name="IncidentTypePicker">
<Picker.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>Accident</x:String>
<x:String>Crime</x:String>
<x:String>Fire</x:String>
<x:String>Other</x:String>
</x:Array>
</Picker.ItemsSource>
</Picker>
<!-- Description -->
<Label Text="Description" FontSize="Medium" />
<Editor x:Name="DescriptionEditor" Placeholder="Enter incident details" AutoSize="TextChanges" />
<!-- Location -->
<Label Text="Location (Auto-filled)" FontSize="Medium" />
<Entry x:Name="LocationEntry" IsReadOnly="True" />
<!-- Attachment -->
<Label Text="Attachment (Optional)" FontSize="Medium" />
<Button Text="Choose File" Clicked="OnChooseFileClicked" />
<!-- Submit Button -->
<Button Text="Submit Report" Clicked="OnSubmitReportClicked" />
</StackLayout>
</ScrollView>
</ContentPage>

@ -1,9 +1,109 @@
namespace Justice.Views;
using ChildGuard.Services;
using Justice.Helpers;
using Justice.Models;
using System;
public partial class IncidentReportPage : ContentPage
namespace Justice.Views
{
public IncidentReportPage()
{
InitializeComponent();
}
}
public partial class IncidentReportPage : ContentPage
{
private readonly DatabaseHelper _databaseHelper;
private readonly EmailService _emailService;
public IncidentReportPage()
{
InitializeComponent();
_databaseHelper = new DatabaseHelper();
_emailService = new EmailService();
}
protected override async void OnAppearing()
{
base.OnAppearing();
await FetchLocationAsync();
}
private async Task FetchLocationAsync()
{
try
{
var (latitude, longitude, address) = await GeolocationHelper.GetLocationAsync();
LocationEntry.Text = address;
}
catch (Exception ex)
{
LocationEntry.Text = "Unable to fetch location.";
Console.WriteLine($"Error fetching location: {ex.Message}");
}
}
private async void OnSubmitReportClicked(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(ReporterNameEntry.Text) ||
IncidentTypePicker.SelectedItem == null ||
string.IsNullOrWhiteSpace(DescriptionEditor.Text))
{
await DisplayAlert("Error", "Please fill in all required fields.", "OK");
return;
}
var (latitude, longitude, address) = await GeolocationHelper.GetLocationAsync();
var report = new IncidentReport
{
ReporterName = ReporterNameEntry.Text.Trim(),
IncidentType = IncidentTypePicker.SelectedItem.ToString(),
Description = DescriptionEditor.Text.Trim(),
Latitude = latitude,
Longitude = longitude,
Address = address,
DateTime = DateTime.Now,
AttachmentPath = null // Optional
};
try
{
// Save to database
await _databaseHelper.InsertAsync(report);
// Get recipient email based on incident type
string recipientEmail = _emailService.GetRecipientEmail(report.IncidentType);
// Prepare email content
string emailBody = $"Incident Report:\n\n" +
$"Name: {report.ReporterName}\n" +
$"Type: {report.IncidentType}\n" +
$"Description: {report.Description}\n" +
$"Latitude: {report.Latitude}\n" +
$"Longitude: {report.Longitude}\n" +
$"Location: {report.Address}\n" +
$"Date/Time: {report.DateTime}";
// Send email
bool emailSent = await _emailService.SendEmailAsync(recipientEmail, "Incident Report", emailBody);
if (emailSent)
{
await DisplayAlert("Success", "Incident report submitted and email alert sent.", "OK");
}
else
{
await DisplayAlert("Error", "Failed to send email alert.", "OK");
}
}
catch (Exception ex)
{
await DisplayAlert("Error", $"Failed to submit report: {ex.Message}", "OK");
}
}
private async void OnChooseFileClicked(object sender, EventArgs e)
{
var result = await FilePicker.PickAsync();
if (result != null)
{
await DisplayAlert("File Selected", $"File: {result.FileName}", "OK");
}
}
}
}

@ -4,11 +4,33 @@
<name>Justice</name>
</assembly>
<members>
<member name="M:Justice.Helpers.GeolocationHelper.GetLocationAsync">
<summary>
Fetches the user's current latitude, longitude, and readable address.
</summary>
<returns>A tuple containing Latitude, Longitude, and Address.</returns>
</member>
<member name="T:Justice.Resource">
<summary>
Android Resource Designer class.
Exposes the Android Resource designer assembly into the project Namespace.
</summary>
</member>
<member name="M:ChildGuard.Services.EmailService.SendEmailAsync(System.String,System.String,System.String)">
<summary>
Sends an email using SMTP.
</summary>
<param name="recipientEmail">Recipient's email address.</param>
<param name="subject">Email subject.</param>
<param name="body">Email body.</param>
<returns>True if successful, otherwise false.</returns>
</member>
<member name="M:ChildGuard.Services.EmailService.GetRecipientEmail(System.String)">
<summary>
Gets the recipient email based on the incident type.
</summary>
<param name="incidentType">Incident type.</param>
<returns>Recipient email address.</returns>
</member>
</members>
</doc>

@ -9,6 +9,7 @@
".NETCoreApp,Version=v9.0/win10-x64": {
"Justice/1.0": {
"dependencies": {
"MailKit": "4.9.0",
"Microsoft.Extensions.Logging.Debug": "9.0.0",
"Microsoft.Maui.Controls": "9.0.22",
"Microsoft.Maui.Maps": "9.0.22",
@ -35,6 +36,26 @@
}
}
},
"BouncyCastle.Cryptography/2.5.0": {
"runtime": {
"lib/net6.0/BouncyCastle.Cryptography.dll": {
"assemblyVersion": "2.0.0.0",
"fileVersion": "2.5.0.6119"
}
}
},
"MailKit/4.9.0": {
"dependencies": {
"MimeKit": "4.9.0",
"System.Formats.Asn1": "8.0.1"
},
"runtime": {
"lib/net8.0/MailKit.dll": {
"assemblyVersion": "4.9.0.0",
"fileVersion": "4.9.0.0"
}
}
},
"Microsoft.Extensions.Configuration/9.0.0": {
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "9.0.0",
@ -454,6 +475,19 @@
}
}
},
"MimeKit/4.9.0": {
"dependencies": {
"BouncyCastle.Cryptography": "2.5.0",
"System.Formats.Asn1": "8.0.1",
"System.Security.Cryptography.Pkcs": "8.0.1"
},
"runtime": {
"lib/net8.0/MimeKit.dll": {
"assemblyVersion": "4.9.0.0",
"fileVersion": "4.9.0.0"
}
}
},
"sqlite-net-pcl/1.9.172": {
"dependencies": {
"SQLitePCLRaw.bundle_green": "2.1.2"
@ -506,7 +540,16 @@
}
}
},
"System.Formats.Asn1/8.0.1": {},
"System.Memory/4.5.3": {},
"System.Security.Cryptography.Pkcs/8.0.1": {
"runtime": {
"runtimes/win/lib/net8.0/System.Security.Cryptography.Pkcs.dll": {
"assemblyVersion": "8.0.0.0",
"fileVersion": "8.0.1024.46610"
}
}
},
"Microsoft.Maui.Controls.HotReload.Forms/17.0.0.0": {
"runtime": {
"Microsoft.Maui.Controls.HotReload.Forms.dll": {
@ -552,6 +595,20 @@
"serviceable": false,
"sha512": ""
},
"BouncyCastle.Cryptography/2.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-rc7vRCq/KD3GtIwSgRtjanGaBwTb9nLenFDZnEcauWlssuuEoxcbMfWA3QWWho6QDMSOSkWjs657McdHzEtEcw==",
"path": "bouncycastle.cryptography/2.5.0",
"hashPath": "bouncycastle.cryptography.2.5.0.nupkg.sha512"
},
"MailKit/4.9.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-wRUVzYXRWbjdUixwtSp7ivVHPhWw17h+BvwIVaouBike68B56Xk9b5+QRT39EK/QN8XF7MtOgt9caHvUaCvhIQ==",
"path": "mailkit/4.9.0",
"hashPath": "mailkit.4.9.0.nupkg.sha512"
},
"Microsoft.Extensions.Configuration/9.0.0": {
"type": "package",
"serviceable": true,
@ -720,6 +777,13 @@
"path": "microsoft.windowsappsdk/1.6.241114003",
"hashPath": "microsoft.windowsappsdk.1.6.241114003.nupkg.sha512"
},
"MimeKit/4.9.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-DZXXMZzmAABDxFhOSMb6SE8KKxcRd/sk1E6aJTUE5ys2FWOQhznYV2Gl3klaaSfqKn27hQ32haqquH1J8Z6kJw==",
"path": "mimekit/4.9.0",
"hashPath": "mimekit.4.9.0.nupkg.sha512"
},
"sqlite-net-pcl/1.9.172": {
"type": "package",
"serviceable": true,
@ -755,6 +819,13 @@
"path": "sqlitepclraw.provider.e_sqlite3/2.1.2",
"hashPath": "sqlitepclraw.provider.e_sqlite3.2.1.2.nupkg.sha512"
},
"System.Formats.Asn1/8.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-XqKba7Mm/koKSjKMfW82olQdmfbI5yqeoLV/tidRp7fbh5rmHAQ5raDI/7SU0swTzv+jgqtUGkzmFxuUg0it1A==",
"path": "system.formats.asn1/8.0.1",
"hashPath": "system.formats.asn1.8.0.1.nupkg.sha512"
},
"System.Memory/4.5.3": {
"type": "package",
"serviceable": true,
@ -762,6 +833,13 @@
"path": "system.memory/4.5.3",
"hashPath": "system.memory.4.5.3.nupkg.sha512"
},
"System.Security.Cryptography.Pkcs/8.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-CoCRHFym33aUSf/NtWSVSZa99dkd0Hm7OCZUxORBjRB16LNhIEOf8THPqzIYlvKM0nNDAPTRBa1FxEECrgaxxA==",
"path": "system.security.cryptography.pkcs/8.0.1",
"hashPath": "system.security.cryptography.pkcs.8.0.1.nupkg.sha512"
},
"Microsoft.Maui.Controls.HotReload.Forms/17.0.0.0": {
"type": "reference",
"serviceable": false,

Loading…
Cancel
Save