@ -0,0 +1,42 @@ |
|||||||
|
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" |
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" |
||||||
|
x:Class="Justice.Views.MapTimer" |
||||||
|
Title="Track Journey"> |
||||||
|
<VerticalStackLayout Padding="20" Spacing="15"> |
||||||
|
<!-- Page Title --> |
||||||
|
<Label Text="Track Your Journey" FontSize="Large" HorizontalOptions="Center" FontAttributes="Bold" /> |
||||||
|
|
||||||
|
<!-- Current Location --> |
||||||
|
<Label Text="Current Location (Latitude, Longitude):" FontSize="Medium" /> |
||||||
|
<Entry x:Name="SourceLatitudeEntry" IsReadOnly="True" Placeholder="Latitude" /> |
||||||
|
<Entry x:Name="SourceLongitudeEntry" IsReadOnly="True" Placeholder="Longitude" /> |
||||||
|
<Entry x:Name="SourceAddressEntry" IsReadOnly="True" Placeholder="Address" /> |
||||||
|
|
||||||
|
<!-- Destination Location --> |
||||||
|
<Label Text="Destination Location (Latitude, Longitude):" FontSize="Medium" /> |
||||||
|
<Entry x:Name="DestinationLatitudeEntry" Placeholder="Enter Destination Latitude" /> |
||||||
|
<Entry x:Name="DestinationLongitudeEntry" Placeholder="Enter Destination Longitude" /> |
||||||
|
|
||||||
|
<!-- Timer --> |
||||||
|
<Label Text="Set Timer (minutes):" FontSize="Medium" /> |
||||||
|
<Entry x:Name="TimerEntry" Keyboard="Numeric" Placeholder="Enter timer in minutes" /> |
||||||
|
|
||||||
|
<!-- Countdown Display --> |
||||||
|
<Label x:Name="TimerLabel" |
||||||
|
Text="Set your timer and start tracking." |
||||||
|
FontSize="Medium" |
||||||
|
HorizontalOptions="Center" |
||||||
|
VerticalOptions="CenterAndExpand" |
||||||
|
TextColor="DarkGray" /> |
||||||
|
|
||||||
|
<!-- Start Button --> |
||||||
|
<Button Text="Start Tracking" Clicked="StartTrackingClicked" /> |
||||||
|
|
||||||
|
<!-- Activity Indicator --> |
||||||
|
<ActivityIndicator x:Name="LoadingIndicator" |
||||||
|
IsRunning="False" |
||||||
|
IsVisible="False" |
||||||
|
HorizontalOptions="Center" |
||||||
|
VerticalOptions="Center" /> |
||||||
|
</VerticalStackLayout> |
||||||
|
</ContentPage> |
@ -0,0 +1,142 @@ |
|||||||
|
using Justice.Helpers; |
||||||
|
using Microsoft.Maui.ApplicationModel.Communication; |
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Justice.Views |
||||||
|
{ |
||||||
|
public partial class MapTimer : ContentPage |
||||||
|
{ |
||||||
|
private bool isTrackingActive; |
||||||
|
|
||||||
|
public MapTimer() |
||||||
|
{ |
||||||
|
InitializeComponent(); |
||||||
|
FetchLocation(); |
||||||
|
} |
||||||
|
|
||||||
|
private async void StartTrackingClicked(object sender, EventArgs e) |
||||||
|
{ |
||||||
|
if (string.IsNullOrWhiteSpace(DestinationLatitudeEntry.Text) || |
||||||
|
string.IsNullOrWhiteSpace(DestinationLongitudeEntry.Text)) |
||||||
|
{ |
||||||
|
await DisplayAlert("Error", "Please enter destination coordinates.", "OK"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (!double.TryParse(DestinationLatitudeEntry.Text, out double destinationLatitude) || |
||||||
|
!double.TryParse(DestinationLongitudeEntry.Text, out double destinationLongitude)) |
||||||
|
{ |
||||||
|
await DisplayAlert("Error", "Invalid destination coordinates.", "OK"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (!int.TryParse(TimerEntry.Text, out int timerMinutes)) |
||||||
|
{ |
||||||
|
await DisplayAlert("Error", "Invalid timer value. Please enter a numeric value.", "OK"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
isTrackingActive = true; |
||||||
|
|
||||||
|
// Set timer duration |
||||||
|
var timerDuration = TimeSpan.FromMinutes(timerMinutes); |
||||||
|
var timerEnd = DateTime.Now.Add(timerDuration); |
||||||
|
string phoneNumber = "9860104625"; |
||||||
|
|
||||||
|
// Start countdown display |
||||||
|
_ = UpdateCountdownDisplay(timerEnd); |
||||||
|
|
||||||
|
while (isTrackingActive && DateTime.Now < timerEnd) |
||||||
|
{ |
||||||
|
await Task.Delay(5000); // Check location every 5 seconds |
||||||
|
|
||||||
|
// Fetch current location |
||||||
|
var (currentLatitude, currentLongitude, _) = await GeolocationHelper.GetLocationAsync(); |
||||||
|
|
||||||
|
// Define tolerance for proximity |
||||||
|
const double Tolerance = 0.0001; |
||||||
|
|
||||||
|
// Check if user has reached the destination |
||||||
|
if (Math.Abs(destinationLatitude - currentLatitude) < Tolerance && |
||||||
|
Math.Abs(destinationLongitude - currentLongitude) < Tolerance) |
||||||
|
{ |
||||||
|
isTrackingActive = false; |
||||||
|
|
||||||
|
// Notify user and send SMS |
||||||
|
string reachedMessage = "User has safely reached the destination."; |
||||||
|
await DisplayAlert("Success", "Safely reached the destination!", "OK"); |
||||||
|
await SendSmsNotification(phoneNumber, reachedMessage); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// If timer expires and destination is not reached |
||||||
|
if (isTrackingActive) |
||||||
|
{ |
||||||
|
string notReachedMessage = "User did not reach the destination within the set time."; |
||||||
|
await DisplayAlert("Time's Up", "You did not reach the destination within the set timer.", "OK"); |
||||||
|
await SendSmsNotification(phoneNumber, notReachedMessage); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private async Task UpdateCountdownDisplay(DateTime timerEnd) |
||||||
|
{ |
||||||
|
while (isTrackingActive && DateTime.Now < timerEnd) |
||||||
|
{ |
||||||
|
TimeSpan remainingTime = timerEnd - DateTime.Now; |
||||||
|
|
||||||
|
// Update the UI with remaining time |
||||||
|
TimerLabel.Text = $"Time Remaining: {remainingTime.Minutes:D2}:{remainingTime.Seconds:D2}"; |
||||||
|
|
||||||
|
// Wait for 1 second before updating again |
||||||
|
await Task.Delay(1000); |
||||||
|
} |
||||||
|
|
||||||
|
// Clear the timer label after countdown ends |
||||||
|
TimerLabel.Text = isTrackingActive ? "Time's Up!" : "You reached the destination!"; |
||||||
|
} |
||||||
|
|
||||||
|
private async void FetchLocation() |
||||||
|
{ |
||||||
|
try |
||||||
|
{ |
||||||
|
var (latitude, longitude, address) = await GeolocationHelper.GetLocationAsync(); |
||||||
|
SourceLatitudeEntry.Text = latitude.ToString(); |
||||||
|
SourceLongitudeEntry.Text = longitude.ToString(); |
||||||
|
} |
||||||
|
catch (Exception ex) |
||||||
|
{ |
||||||
|
Console.WriteLine($"Error fetching location: {ex.Message}"); |
||||||
|
await DisplayAlert("Error", $"Failed to get location: {ex.Message}", "OK"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private async Task SendSmsNotification(string phoneNumber, string message) |
||||||
|
{ |
||||||
|
try |
||||||
|
{ |
||||||
|
if (Sms.Default.IsComposeSupported) |
||||||
|
{ |
||||||
|
var smsMessage = new SmsMessage |
||||||
|
{ |
||||||
|
Body = message, |
||||||
|
Recipients = new List<string> { phoneNumber } |
||||||
|
}; |
||||||
|
|
||||||
|
await Sms.Default.ComposeAsync(smsMessage); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
await DisplayAlert("Error", "SMS is not supported on this device.", "OK"); |
||||||
|
} |
||||||
|
} |
||||||
|
catch (Exception ex) |
||||||
|
{ |
||||||
|
Console.WriteLine($"Error sending SMS: {ex.Message}"); |
||||||
|
await DisplayAlert("Error", "Failed to send SMS.", "OK"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 114 KiB |
Before Width: | Height: | Size: 301 KiB After Width: | Height: | Size: 477 KiB |
After Width: | Height: | Size: 76 KiB |
After Width: | Height: | Size: 101 KiB |
After Width: | Height: | Size: 124 KiB |
After Width: | Height: | Size: 186 KiB |
After Width: | Height: | Size: 427 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 59 KiB |
After Width: | Height: | Size: 106 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 92 KiB |
After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 7.9 KiB |
Before Width: | Height: | Size: 21 KiB |