diff --git a/Covid19Radar/Covid19Radar.Android/Covid19Radar.Android.csproj b/Covid19Radar/Covid19Radar.Android/Covid19Radar.Android.csproj
index 567133036..b509ba183 100644
--- a/Covid19Radar/Covid19Radar.Android/Covid19Radar.Android.csproj
+++ b/Covid19Radar/Covid19Radar.Android/Covid19Radar.Android.csproj
@@ -39,7 +39,7 @@
SdkOnly
false
true
- false
+ true
CJK
armeabi-v7a;x86;x86_64;arm64-v8a
true
@@ -112,12 +112,13 @@
prompt
MinimumRecommendedRules.ruleset
true
- false
true
+ true
apk
true
CJK
Xamarin.Android.Net.AndroidClientHandler
+ None
diff --git a/Covid19Radar/Covid19Radar/App.xaml.cs b/Covid19Radar/Covid19Radar/App.xaml.cs
index 7100d5450..95c5f98fe 100644
--- a/Covid19Radar/Covid19Radar/App.xaml.cs
+++ b/Covid19Radar/Covid19Radar/App.xaml.cs
@@ -128,6 +128,9 @@ protected override void RegisterTypes(IContainerRegistry containerRegistry)
containerRegistry.RegisterForNavigation();
containerRegistry.RegisterForNavigation();
containerRegistry.RegisterForNavigation();
+#if DEBUG
+ containerRegistry.RegisterForNavigation();
+#endif
// Settings
containerRegistry.RegisterForNavigation();
diff --git a/Covid19Radar/Covid19Radar/Covid19Radar.csproj b/Covid19Radar/Covid19Radar/Covid19Radar.csproj
index 9a434c0c5..0ac7e11a1 100644
--- a/Covid19Radar/Covid19Radar/Covid19Radar.csproj
+++ b/Covid19Radar/Covid19Radar/Covid19Radar.csproj
@@ -153,4 +153,9 @@
+
+
+
+
+
diff --git a/Covid19Radar/Covid19Radar/Services/HttpDataServiceMock.cs b/Covid19Radar/Covid19Radar/Services/HttpDataServiceMock.cs
index fe7a9dd1f..31ce67b5f 100644
--- a/Covid19Radar/Covid19Radar/Services/HttpDataServiceMock.cs
+++ b/Covid19Radar/Covid19Radar/Services/HttpDataServiceMock.cs
@@ -3,17 +3,43 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
using Covid19Radar.Model;
+using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
+using System.Linq;
+using System.Net.Http;
+using Covid19Radar.Common;
+using Newtonsoft.Json;
namespace Covid19Radar.Services
{
class HttpDataServiceMock : IHttpDataService
{
+ private readonly HttpClient downloadClient;
+ private readonly MockCommonUtils mockCommonUtils = new MockCommonUtils();
+
+ public HttpDataServiceMock(IHttpClientService httpClientService)
+ {
+ downloadClient = httpClientService.Create();
+ }
+
+ // copy from ./HttpDataService.cs
+ private async Task GetCdnAsync(string url, CancellationToken cancellationToken)
+ {
+ HttpResponseMessage result = await downloadClient.GetAsync(url, cancellationToken);
+ await result.Content.ReadAsStringAsync();
+
+ if (result.StatusCode == System.Net.HttpStatusCode.OK)
+ {
+ return await result.Content.ReadAsStringAsync();
+ }
+ return null;
+ }
+
public Task MigrateFromUserData(UserDataModel userData)
{
return Task.CompletedTask;
@@ -37,27 +63,119 @@ Task IHttpDataService.GetTemporaryExposureKey(string url, CancellationTo
});
}
- Task> IHttpDataService.GetTemporaryExposureKeyList(string region, CancellationToken cancellationToken)
+ private TemporaryExposureKeyExportFileModel CreateTestData(long created)
{
- return Task.Factory.StartNew>(() =>
+ return new TemporaryExposureKeyExportFileModel()
{
- Debug.WriteLine("HttpDataServiceMock::GetTemporaryExposureKeyList called");
- return new List();
- });
+ Region = "440",
+ Url = "testUrl",
+ Created = created
+ };
+ }
+
+ private long CalcTimeAddDays(int day)
+ => new DateTimeOffset(DateTime.UtcNow.AddDays(day)).ToUnixTimeMilliseconds();
+
+ private long CalcMidnightTimeAddDays(int day)
+ {
+ DateTime d = DateTime.UtcNow.AddDays(day);
+ // set 0 hour,1 min,2 sec,3 millisecond for debug
+ return new DateTimeOffset(new DateTime(d.Year, d.Month, d.Day, 0, 1, 2, 3)).ToUnixTimeMilliseconds();
}
+ enum PresetTekListType // PresetTekListData for Tek List
+ {
+ Nothing = 0, //nothing (default for v1.2.3)
+ RealTime = 1, // real time
+ MidnightTime = 2, // last night
+ // please add "YourDataType = "
+ }
+
+ private List PresetTekListData(int dataVersion)
+ {
+ switch ((PresetTekListType)dataVersion)
+ {
+ case PresetTekListType.MidnightTime:
+ return new List { CreateTestData(CalcMidnightTimeAddDays(-1)), CreateTestData(CalcMidnightTimeAddDays(0)) };
+ case PresetTekListType.RealTime:
+ return new List { CreateTestData(CalcTimeAddDays(-1)), CreateTestData(CalcTimeAddDays(0)) };
+ case PresetTekListType.Nothing:
+ default:
+ return new List();
+ }
+ }
+
+ async Task> IHttpDataService.GetTemporaryExposureKeyList(string region, CancellationToken cancellationToken)
+ {
+ /* CdnUrlBase trick for Debug_Mock
+ "https://www.example.com/"(url with 2+ periods) -> download "url"+"c19r/440/list.json". IsDownloadRequired
+ "1598022036649,1598022036751,1598022036826" -> direct input timestamps. IsDirectInput
+ "https://CDN_URL_BASE/2" -> dataVersion = 2
+ "https://CDN_URL_BASE/" -> dataVersion = 0 (default)
+ */
+ //string url = AppSettings.Instance.CdnUrlBase;
+ if (mockCommonUtils.IsDownloadRequired())
+ {
+ // copy from GetTemporaryExposureKeyList @ ./HttpDataService.cs and delete logger part
+ var container = AppSettings.Instance.BlobStorageContainerName;
+ var urlJson = AppSettings.Instance.CdnUrlBase + $"{container}/{region}/list.json";
+ var result = await GetCdnAsync(urlJson, cancellationToken);
+ if (result != null)
+ {
+ Debug.WriteLine("HttpDataServiceMock::GetTemporaryExposureKeyList downloaded");
+ return JsonConvert.DeserializeObject>(result);
+ }
+ else
+ {
+ Debug.WriteLine("HttpDataServiceMock::GetTemporaryExposureKeyList download failed");
+ return new List();
+ }
+ }
+ else if (mockCommonUtils.IsDirectInput())
+ {
+ Debug.WriteLine("HttpDataServiceMock::GetTemporaryExposureKeyList direct data called");
+ return (mockCommonUtils.GetCreatedTimes().Select(x => CreateTestData(Convert.ToInt64(x))).ToList());
+ }
+ else
+ {
+ Debug.WriteLine("HttpDataServiceMock::GetTemporaryExposureKeyList preset data called");
+ return PresetTekListData(mockCommonUtils.GetTekListDataType());
+ }
+ }
+
+
async Task IHttpDataService.PostRegisterUserAsync()
{
Debug.WriteLine("HttpDataServiceMock::PostRegisterUserAsync called");
- return await Task.FromResult(true);
+ var result = mockCommonUtils.GetRegisterDataType() switch
+ {
+ 1 => false,
+ _ => true
+ };
+ return await Task.FromResult(result);
}
Task IHttpDataService.PutSelfExposureKeysAsync(DiagnosisSubmissionParameter request)
{
+ var code = HttpStatusCode.OK; // default. for PutSelfExposureKeys NG
+ var dataType = mockCommonUtils.GetDiagnosisDataType();
+ if (dataType >= 100) // HttpStatusCode >=100 by RFC2616#section-10
+ {
+ code = (HttpStatusCode)dataType;
+ }
+ else
+ {
+ switch (dataType)
+ {
+ case 1:
+ code = HttpStatusCode.NoContent; // for Successful PutSelfExposureKeys
+ break;
+ }
+ }
return Task.Factory.StartNew(() =>
{
Debug.WriteLine("HttpDataServiceMock::PutSelfExposureKeysAsync called");
- return HttpStatusCode.OK;
+ return code;
});
}
diff --git a/Covid19Radar/Covid19Radar/Services/TestNativeImplementation.cs b/Covid19Radar/Covid19Radar/Services/TestNativeImplementation.cs
index f32720ee3..c44356fec 100644
--- a/Covid19Radar/Covid19Radar/Services/TestNativeImplementation.cs
+++ b/Covid19Radar/Covid19Radar/Services/TestNativeImplementation.cs
@@ -4,79 +4,200 @@
using System;
using System.Collections.Generic;
+using System.Linq;
+using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Xamarin.Essentials;
using Xamarin.ExposureNotifications;
namespace Covid19Radar.Services
{
- public class TestNativeImplementation : INativeImplementation
- {
- static readonly Random random = new Random();
-
- Task WaitRandom()
- => Task.Delay(random.Next(100, 2500));
-
- public async Task StartAsync()
- {
- await WaitRandom();
- Preferences.Set("fake_enabled", true);
- }
-
- public async Task StopAsync()
- {
- await WaitRandom();
- Preferences.Set("fake_enabled", true);
- }
-
- public async Task IsEnabledAsync()
- {
- await WaitRandom();
- return Preferences.Get("fake_enabled", true);
- }
-
- public async Task> GetSelfTemporaryExposureKeysAsync()
- {
- var keys = new List();
-
- for (var i = 1; i < 14; i++)
- keys.Add(GenerateRandomKey(i));
-
- await WaitRandom();
-
- return keys;
- }
-
- public Task GetStatusAsync()
- => Task.FromResult(Preferences.Get("fake_enabled", true) ? Status.Active : Status.Disabled);
-
- public Task<(ExposureDetectionSummary summary, Func>> getInfo)> DetectExposuresAsync(IEnumerable files)
- {
- var summary = new ExposureDetectionSummary(10, 2, 5);
-
- Task> GetInfo()
- {
- var info = new List
- {
- new ExposureInfo (DateTime.UtcNow.AddDays(-10), TimeSpan.FromMinutes(15), 65, 5, RiskLevel.Medium),
- new ExposureInfo (DateTime.UtcNow.AddDays(-11), TimeSpan.FromMinutes(5), 40, 3, RiskLevel.Low),
- };
- return Task.FromResult>(info);
- }
-
- return Task.FromResult<(ExposureDetectionSummary, Func>>)>((summary, GetInfo));
- }
-
- static TemporaryExposureKey GenerateRandomKey(int daysAgo)
- {
- var buffer = new byte[16];
- random.NextBytes(buffer);
-
- return new TemporaryExposureKey(
- buffer,
- DateTimeOffset.UtcNow.AddDays(-1 * daysAgo),
- TimeSpan.FromMinutes(random.Next(5, 120)),
- (RiskLevel)random.Next(1, 8));
- }
- }
+ public class MockCommonUtils
+ {
+ public string CdnUrlBase { get => AppSettings.Instance.CdnUrlBase; }
+ public string ApiUrlBase { get => AppSettings.Instance.ApiUrlBase; }
+
+ public bool IsDownloadRequired()
+ => Regex.IsMatch(CdnUrlBase, @"^https://.*\..*\..*/$");
+
+ public bool IsDirectInput()
+ => Regex.IsMatch(CdnUrlBase, @"^(\d+,)+\d+,*$");
+
+
+ private ushort NumberEndofSentence(string url)
+ {
+ Match match = Regex.Match(url, @"(?\d+)$");
+ ushort number = 0;
+ if (match.Success)
+ {
+ number = Convert.ToUInt16(match.Groups["d"].Value);
+ }
+ return number;
+ }
+ public List GetCreatedTimes()
+ => CdnUrlBase.Split(",").ToList();
+ public ushort GetTekListDataType()
+ => NumberEndofSentence(CdnUrlBase);
+ public string[] GetApiUrlSegment()
+ {
+ // "url/api" -> { "url/api", "", "" }
+ // "url/base/api/register1/diagnosis2" -> { "url/base/api", "/register1", "/diagnosis2" }
+ // "url/api1/r1/d2" -> { "url/api1", "/r1", "/d2" }
+ // "url/api1/d2/r1" -> { "url/api1", "/r1", "/d2" }
+ var url = ApiUrlBase;
+ var r = new Regex("/r(egister)?[0-9]+");
+ var d = new Regex("/d(iagnosis)?[0-9]+");
+ var urlRegister = r.Match(url).Value;
+ url = r.Replace(url, "");
+ var urlDiagnosis = d.Match(url).Value;
+ url = d.Replace(url, "");
+ var urlApi = url;
+ return new string[] { urlApi, urlRegister, urlDiagnosis };
+ }
+ public ushort GetDiagnosisDataType()
+ => NumberEndofSentence(GetApiUrlSegment()[2]);
+ public ushort GetRegisterDataType()
+ => NumberEndofSentence(GetApiUrlSegment()[1]);
+ public ushort GetApiDataType()
+ => NumberEndofSentence(GetApiUrlSegment()[0]);
+ public bool IsDirectInputApi()
+ => Regex.IsMatch(GetApiUrlSegment()[0], @"^(\d+,)+\d+,?$");
+ public List GetApiStrings()
+ => GetApiUrlSegment()[0].Split(",").ToList();
+ }
+ public class TestNativeImplementation : INativeImplementation
+ {
+ static readonly Random random = new Random();
+ private readonly MockCommonUtils mockCommonUtils = new MockCommonUtils();
+ const int DAY_OF_TEK_STORED = 14; // used only in GetSelfTemporaryExposureKeysAsync
+ const int KEY_DATA_LENGTH = 16; // used only in GenerateRandomKey
+
+ Task WaitRandom()
+ => Task.Delay(random.Next(100, 2500));
+
+ public async Task StartAsync()
+ {
+ await WaitRandom();
+ Preferences.Set("fake_enabled", true);
+ }
+
+ public async Task StopAsync()
+ {
+ await WaitRandom();
+ Preferences.Set("fake_enabled", false);
+ }
+
+ public async Task IsEnabledAsync()
+ {
+ await WaitRandom();
+ return Preferences.Get("fake_enabled", true);
+ }
+
+ public async Task> GetSelfTemporaryExposureKeysAsync()
+ {
+ var keys = new List();
+ for (var i = 1; i <= DAY_OF_TEK_STORED; i++)
+ {
+ keys.Add(GenerateRandomKey(i));
+ }
+ await WaitRandom();
+
+ return keys;
+ }
+
+ public Task GetStatusAsync()
+ => Task.FromResult(Preferences.Get("fake_enabled", true) ? Status.Active : Status.Disabled);
+
+ enum PresetDataType
+ {
+ TwoLowRiskMatches = 0, // two low-risk matches (default for v1.2.3)
+ OneHighRiskMatchAnd2LowRiskMatches = 1, // one high-risk match and 2 low-risk matches
+ NoMatch = 2,
+ // please add "YourDataType = "
+ }
+
+ private ushort[] DataPreset(int dataType)
+ {
+ /* DataPreset returns ushort[];
+ index[0] ~ index[4] : data for ExposureDetectionSummary
+ index[5] ~ index[10] : 1st data for ExposureInfo
+ index[11] ~ index[15] : 2nd data for ExposureInfo
+ index[16] ~ index[20] : 3rd data for ExposureInfo
+ ....
+ */
+ switch ((PresetDataType)dataType)
+ {
+ case PresetDataType.OneHighRiskMatchAnd2LowRiskMatches:
+ return (
+ new ushort[] {10, 3, 27, 0, 0, // ExposureDetectionSummary
+ 13, 15, 65, 27, (ushort)RiskLevel.High, // ExposureInfo 1st
+ 10, 15, 65, 5, (ushort)RiskLevel.Medium, // ExposureInfo 2st
+ 11, 5, 40, 3, (ushort)RiskLevel.Low, // ExposureInfo 3nd
+ });
+ case PresetDataType.NoMatch:
+ return (
+ new ushort[] {0, 0, 0, 0, 0, // ExposureDetectionSummary
+ });
+ case PresetDataType.TwoLowRiskMatches:
+ default:
+ return (
+ new ushort[] {10, 2, 5, 0, 0, // ExposureDetectionSummary
+ 10, 15, 65, 5, (ushort)RiskLevel.Medium, // ExposureInfo 1st (RiskLevel.Medium=4)
+ 11, 5, 40, 3, (ushort)RiskLevel.Low, // ExposureInfo 2nd(RiskLevel.Low=2)
+ });
+ }
+ }
+
+ private ushort[] CreatePresetData()
+ {
+ if (mockCommonUtils.IsDirectInputApi())
+ {
+ return mockCommonUtils.GetApiStrings().Select(x => Convert.ToUInt16(x)).ToArray();
+ }
+ return DataPreset(mockCommonUtils.GetApiDataType());
+
+ }
+
+ public Task<(ExposureDetectionSummary summary, Func>> getInfo)> DetectExposuresAsync(IEnumerable files)
+ {
+ /* ApiUrlBase trick for Debug_Mock
+ "10,2,5,0,0,10,15,65,5,4,11,5,40,3,2" -> direct input (the same with default)
+ "https://API_URL_BASE/api2" -> dataVer = 2
+ "https://API_URL_BASE/api" -> dataVer = 0 (default)
+ others -> dataVer is the number at the end of the sentence
+ */
+ var dataPreset = CreatePresetData();
+ int index = 0;
+ var summary = new ExposureDetectionSummary(dataPreset[index++], dataPreset[index++],
+ dataPreset[index++], new TimeSpan[dataPreset[index++]], dataPreset[index++]);
+ // c.f.: ExposureDetectionSummary(daysSinceLastExposure=dataPreset[0],matchedKeyCount=dataPreset[1],maximumRiskScore=dataPreset[2],attenuationDurations=new TimeSpan[dataPreset[3]],summationRiskScore=dataPreset[4])
+
+ Task> GetInfo()
+ {
+ var info = new List();
+ while (index < dataPreset.Length)
+ {
+ info.Add(new ExposureInfo(DateTime.UtcNow.AddDays(-dataPreset[index++]),
+ TimeSpan.FromMinutes(dataPreset[index++]), dataPreset[index++],
+ dataPreset[index++], (Xamarin.ExposureNotifications.RiskLevel)dataPreset[index++]));
+ // c.f.: ExposureInfo(DateTime timestamp, TimeSpan duration, int attenuationValue, int totalRiskScore, RiskLevel riskLevel)
+ };
+ return Task.FromResult>(info);
+ }
+
+ return Task.FromResult<(ExposureDetectionSummary, Func>>)>((summary, GetInfo));
+ }
+
+ static TemporaryExposureKey GenerateRandomKey(int daysAgo)
+ {
+ var keyData = new byte[KEY_DATA_LENGTH];
+ random.NextBytes(keyData);
+
+ return new TemporaryExposureKey(
+ keyData,
+ DateTimeOffset.UtcNow.AddDays(-1 * daysAgo),
+ TimeSpan.FromMinutes(random.Next(5, 120)),
+ (RiskLevel)random.Next(1, 8));
+ }
+ }
}
diff --git a/Covid19Radar/Covid19Radar/ViewModels/MenuPageViewModel.cs b/Covid19Radar/Covid19Radar/ViewModels/MenuPageViewModel.cs
index cc187c1c5..714d26011 100644
--- a/Covid19Radar/Covid19Radar/ViewModels/MenuPageViewModel.cs
+++ b/Covid19Radar/Covid19Radar/ViewModels/MenuPageViewModel.cs
@@ -78,6 +78,16 @@ public MenuPageViewModel(INavigationService navigationService) : base(navigation
IconColor = "#019AE8",
TextColor = "#000"
});
+#if DEBUG
+ MenuItems.Add(new MainMenuModel()
+ {
+ Icon = "\uf013",
+ PageName = nameof(DebugPage),
+ Title = "Debug",
+ IconColor = "#019AE8",
+ TextColor = "#000"
+ });
+#endif
NavigateCommand = new DelegateCommand(Navigate);
}
diff --git a/Covid19Radar/Covid19Radar/ViewModels/Settings/DebugPageViewModel.cs b/Covid19Radar/Covid19Radar/ViewModels/Settings/DebugPageViewModel.cs
new file mode 100644
index 000000000..9d6de48f2
--- /dev/null
+++ b/Covid19Radar/Covid19Radar/ViewModels/Settings/DebugPageViewModel.cs
@@ -0,0 +1,192 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+using System;
+using System.Linq;
+using Acr.UserDialogs;
+using Covid19Radar.Services;
+using Prism.Navigation;
+using Xamarin.Forms;
+
+namespace Covid19Radar.ViewModels
+{
+ public class DebugPageViewModel : ViewModelBase
+ {
+ private readonly IUserDataService _userDataService;
+ private readonly ITermsUpdateService _termsUpdateService_;
+ private readonly IExposureNotificationService _exposureNotificationService;
+
+ private string _debugInfo;
+ public string DebugInfo
+ {
+ get { return _debugInfo; }
+ set { SetProperty(ref _debugInfo, value); }
+ }
+
+ public async void UpdateInfo(string exception = "")
+ {
+ string os = Device.RuntimePlatform;
+#if DEBUG
+ os += ",DEBUG";
+#endif
+#if USE_MOCK
+ os += ",USE_MOCK";
+#endif
+
+ // debug info for ./SplashPageViewModel.cs
+ var termsUpdateInfo = await _termsUpdateService_.GetTermsUpdateInfo() ?? new Model.TermsUpdateInfoModel();
+
+ var termsOfServiceUpdateDateTime = "Not Available";
+ if (termsUpdateInfo.TermsOfService != null)
+ {
+ termsOfServiceUpdateDateTime = termsUpdateInfo.TermsOfService.UpdateDateTime.ToString();
+ }
+
+ var privacyPolicyUpdateDateTime = "Not Available";
+ if (termsUpdateInfo.PrivacyPolicy != null)
+ {
+ privacyPolicyUpdateDateTime = termsUpdateInfo.PrivacyPolicy.UpdateDateTime.ToString();
+ }
+
+ var lastProcessTekTimestampList = AppSettings.Instance.SupportedRegions.Select(region =>
+ new LastProcessTekTimestamp()
+ {
+ Region = region,
+ Ticks = _exposureNotificationService.GetLastProcessTekTimestamp(region)
+ }.ToString()
+ );
+
+ string regionString = string.Join(",", AppSettings.Instance.SupportedRegions);
+ string lastProcessTekTimestampsStr = string.Join("\n ", lastProcessTekTimestampList);
+
+ var exposureNotificationStatus = await Xamarin.ExposureNotifications.ExposureNotification.IsEnabledAsync();
+ var exposureNotificationMessage = await _exposureNotificationService.UpdateStatusMessageAsync();
+
+ // ../../settings.json
+ var str = new[] {
+ $"Build: {os}",
+ $"Version: {AppSettings.Instance.AppVersion}",
+ $"Region: {regionString}",
+ $"CdnUrl: {AppSettings.Instance.CdnUrlBase}",
+ $"ApiUrl: {AppSettings.Instance.ApiUrlBase}",
+ $"TermsOfServiceUpdatedDateTime: {termsOfServiceUpdateDateTime}",
+ $"PrivacyPolicyUpdatedDateTime: {privacyPolicyUpdateDateTime}",
+ $"StartDate: {_userDataService.GetStartDate().ToLocalTime().ToString("F")}",
+ $"DaysOfUse: {_userDataService.GetDaysOfUse()}",
+ $"ExposureCount: {_exposureNotificationService.GetExposureCountToDisplay()}",
+ $"LastProcessTekTimestamp: {lastProcessTekTimestampsStr}",
+ $"ENstatus: {exposureNotificationStatus}",
+ $"ENmessage: {exposureNotificationMessage}",
+ $"Now: {DateTime.Now.ToLocalTime().ToString("F")}",
+ exception
+ };
+ DebugInfo = string.Join(Environment.NewLine, str);
+ }
+
+ public DebugPageViewModel(
+ INavigationService navigationService,
+ IUserDataService userDataService,
+ ITermsUpdateService termsUpdateService,
+ IExposureNotificationService exposureNotificationService
+ ) : base(navigationService)
+ {
+ Title = "Title:Debug";
+ _userDataService = userDataService;
+ _termsUpdateService_ = termsUpdateService;
+ _exposureNotificationService = exposureNotificationService;
+ }
+
+ public override void Initialize(INavigationParameters parameters)
+ {
+ base.Initialize(parameters);
+ UpdateInfo("Initialize");
+ }
+
+ public Command OnClickReload => new Command(() => UpdateInfo("Reload"));
+
+ public Command OnClickStartExposureNotification => new Command(async () =>
+ {
+ UserDialogs.Instance.ShowLoading("Starting ExposureNotification...");
+ var result = await _exposureNotificationService.StartExposureNotification();
+ var message = $"Result: {result}";
+ UserDialogs.Instance.HideLoading();
+ await UserDialogs.Instance.AlertAsync(message, "StartExposureNotification", Resources.AppResources.ButtonOk);
+ UpdateInfo("StartExposureNotification");
+ });
+
+ public Command OnClickFetchExposureKeyAsync => new Command(async () =>
+ {
+ var exception = "FetchExposureKeyAsync";
+ try
+ {
+ await _exposureNotificationService.FetchExposureKeyAsync();
+ }
+ catch (Exception ex)
+ {
+ exception += $":Exception: {ex}";
+ }
+ UpdateInfo(exception);
+ });
+
+ // see ../Settings/SettingsPageViewModel.cs
+ public Command OnClickStopExposureNotification => new Command(async () =>
+ {
+ UserDialogs.Instance.ShowLoading("Stopping ExposureNotification...");
+ var result = await _exposureNotificationService.StopExposureNotification();
+ string message = $"Result: {result}";
+ UserDialogs.Instance.HideLoading();
+ await UserDialogs.Instance.AlertAsync(message, "StopExposureNotification", Resources.AppResources.ButtonOk);
+ UpdateInfo("StopExposureNotification");
+ });
+
+ public Command OnClickRemoveStartDate => new Command(() =>
+ {
+ _userDataService.RemoveStartDate();
+ UpdateInfo("RemoveStartDate");
+ });
+
+ public Command OnClickRemoveExposureInformation => new Command(() =>
+ {
+ _exposureNotificationService.RemoveExposureInformation();
+ UpdateInfo("RemoveExposureInformation");
+ });
+
+ public Command OnClickRemoveConfiguration => new Command(() =>
+ {
+ _exposureNotificationService.RemoveConfiguration();
+ UpdateInfo("RemoveConfiguration");
+ });
+
+ public Command OnClickRemoveLastProcessTekTimestamp => new Command(() =>
+ {
+ _exposureNotificationService.RemoveLastProcessTekTimestamp();
+ UpdateInfo("RemoveLastProcessTekTimestamp");
+ });
+
+ public Command OnClickRemoveAllUpdateDate => new Command(() =>
+ {
+ _termsUpdateService_.RemoveAllUpdateDate();
+ UpdateInfo("RemoveAllUpdateDate");
+ });
+
+ public Command OnClickQuit => new Command(() =>
+ {
+ Application.Current.Quit();
+ DependencyService.Get().closeApplication();
+ });
+
+ private class LastProcessTekTimestamp
+ {
+ internal string Region;
+
+ internal long Ticks;
+
+ internal DateTimeOffset DateTime
+ => DateTimeOffset.FromUnixTimeMilliseconds(Ticks).ToLocalTime();
+
+ public override string ToString()
+ => $"{Region} - {DateTime:F}({Ticks})";
+ }
+ }
+}
diff --git a/Covid19Radar/Covid19Radar/Views/Settings/DebugPage.xaml b/Covid19Radar/Covid19Radar/Views/Settings/DebugPage.xaml
new file mode 100644
index 000000000..b69e5090b
--- /dev/null
+++ b/Covid19Radar/Covid19Radar/Views/Settings/DebugPage.xaml
@@ -0,0 +1,212 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Covid19Radar/Covid19Radar/Views/Settings/DebugPage.xaml.cs b/Covid19Radar/Covid19Radar/Views/Settings/DebugPage.xaml.cs
new file mode 100644
index 000000000..aedcb21fa
--- /dev/null
+++ b/Covid19Radar/Covid19Radar/Views/Settings/DebugPage.xaml.cs
@@ -0,0 +1,24 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace Covid19Radar.Views
+{
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+
+ public partial class DebugPage : ContentPage
+ {
+ public DebugPage()
+ {
+ InitializeComponent();
+ }
+ }
+}