From c81e42adf6df123dc841a8a2c02eff0539b685c1 Mon Sep 17 00:00:00 2001 From: ARIYAMA Keiji Date: Sun, 21 Mar 2021 08:19:09 +0900 Subject: [PATCH] Restore change to ExposureNotification.android.cs --- .../ExposureNotification.android.cs | 481 +++++++++--------- .../ExposureNotification.customize.android.cs | 48 -- 2 files changed, 250 insertions(+), 279 deletions(-) delete mode 100644 Covid19Radar/Xamarin.ExposureNotification/ExposureNotification.customize.android.cs diff --git a/Covid19Radar/Xamarin.ExposureNotification/ExposureNotification.android.cs b/Covid19Radar/Xamarin.ExposureNotification/ExposureNotification.android.cs index 029cf7066..55adb741e 100644 --- a/Covid19Radar/Xamarin.ExposureNotification/ExposureNotification.android.cs +++ b/Covid19Radar/Xamarin.ExposureNotification/ExposureNotification.android.cs @@ -22,235 +22,254 @@ namespace Xamarin.ExposureNotifications { - public static partial class ExposureNotification - { - static IExposureNotificationClient instance; - - static IExposureNotificationClient Instance - => instance ??= Nearby.GetExposureNotificationClient(Application.Context); - - static async Task GetConfigurationAsync() - { - var c = await Handler.GetConfigurationAsync(); - - return new ExposureConfiguration.ExposureConfigurationBuilder() - .SetAttenuationScores(c.AttenuationScores) - .SetDurationScores(c.DurationScores) - .SetDaysSinceLastExposureScores(c.DaysSinceLastExposureScores) - .SetTransmissionRiskScores(c.TransmissionRiskScores) - .SetAttenuationWeight(c.AttenuationWeight) - .SetDaysSinceLastExposureWeight(c.DaysSinceLastExposureWeight) - .SetDurationWeight(c.DurationWeight) - .SetTransmissionRiskWeight(c.TransmissionWeight) - .SetMinimumRiskScore(c.MinimumRiskScore) - .SetDurationAtAttenuationThresholds(c.DurationAtAttenuationThresholds) - .Build(); - } - - const int requestCodeStartExposureNotification = 1111; - const int requestCodeGetTempExposureKeyHistory = 2222; - - static TaskCompletionSource tcsResolveConnection; - - public static void OnActivityResult(int requestCode, Result resultCode, global::Android.Content.Intent data) - { - if (requestCode == requestCodeStartExposureNotification || requestCode == requestCodeGetTempExposureKeyHistory) - { - if (resultCode == Result.Ok) - tcsResolveConnection?.TrySetResult(null); - else - tcsResolveConnection.TrySetException(new AccessDeniedException("Failed to resolve Exposure Notifications API")); - } - } - - static async Task ResolveApi(int requestCode, Func> apiCall) - { - try - { - return await apiCall(); - } - catch (ApiException apiEx) - { - if (apiEx.StatusCode == CommonStatusCodes.ResolutionRequired) // Resolution required - { - tcsResolveConnection = new TaskCompletionSource(); - - // Start the resolution - apiEx.Status.StartResolutionForResult(Essentials.Platform.CurrentActivity, requestCode); - - // Wait for the activity result to be called - await tcsResolveConnection.Task; - - // Try the original api call again - return await apiCall(); - } - } - - return default; - } - - static void PlatformInit() - { - _ = ScheduleFetchAsync(); - } - - static Task PlatformStart() - => ResolveApi(requestCodeStartExposureNotification, async () => - { - await Instance.StartAsync(); - return default; - }); - - static Task PlatformStop() - => ResolveApi(requestCodeStartExposureNotification, async () => - { - await Instance.StopAsync(); - return default; - }); - - static Task PlatformIsEnabled() - => ResolveApi(requestCodeStartExposureNotification, () => - Instance.IsEnabledAsync()); - - public static void ConfigureBackgroundWorkRequest(TimeSpan repeatInterval, Action requestBuilder) - { - if (requestBuilder == null) - throw new ArgumentNullException(nameof(requestBuilder)); - if (repeatInterval == null) - throw new ArgumentNullException(nameof(repeatInterval)); - - bgRequestBuilder = requestBuilder; - bgRepeatInterval = repeatInterval; - } - - static Action bgRequestBuilder = b => - b.SetConstraints(new Constraints.Builder() - .SetRequiresBatteryNotLow(true) - .SetRequiresDeviceIdle(true) - .SetRequiredNetworkType(NetworkType.Connected) - .Build()); - - static TimeSpan bgRepeatInterval = TimeSpan.FromHours(6); - - // Tells the local API when new diagnosis keys have been obtained from the server - static async Task PlatformDetectExposuresAsync(IEnumerable keyFiles, System.Threading.CancellationToken cancellationToken) - { - var config = await GetConfigurationAsync(); - - await Instance.ProvideDiagnosisKeysAsync( - keyFiles.Select(f => new Java.IO.File(f)).ToList(), - config, - Guid.NewGuid().ToString()); - } - - static Task> PlatformGetTemporaryExposureKeys() - => ResolveApi(requestCodeGetTempExposureKeyHistory, async () => - { - var exposureKeyHistory = await Instance.GetTemporaryExposureKeyHistoryAsync(); - - return exposureKeyHistory.Select(k => - new TemporaryExposureKey( - k.GetKeyData(), - k.RollingStartIntervalNumber, - TimeSpan.FromMinutes(k.RollingPeriod * 10), - k.TransmissionRiskLevel.FromNative())); - }); - - internal static async Task> PlatformGetExposureInformationAsync(string token) - { - var exposures = await Instance.GetExposureInformationAsync(token); - var info = exposures.Select(d => new ExposureInfo( - DateTimeOffset.UnixEpoch.AddMilliseconds(d.DateMillisSinceEpoch).UtcDateTime, - TimeSpan.FromMinutes(d.DurationMinutes), - d.AttenuationValue, - d.TotalRiskScore, - d.TransmissionRiskLevel.FromNative())); - return info; - } - - internal static async Task PlatformGetExposureSummaryAsync(string token) - { - var summary = await Instance.GetExposureSummaryAsync(token); - - // TODO: Reevaluate byte usage here - return new ExposureDetectionSummary( - summary.DaysSinceLastExposure, - (ulong)summary.MatchedKeyCount, - summary.MaximumRiskScore, - summary.GetAttenuationDurationsInMinutes() - .Select(a => TimeSpan.FromMinutes(a)).ToArray(), - summary.SummationRiskScore); - } - - static async Task PlatformGetStatusAsync() - { - var bt = BluetoothAdapter.DefaultAdapter; - - if (bt == null || !bt.IsEnabled) - return Status.BluetoothOff; - - var status = await Instance.IsEnabledAsync(); - - return status ? Status.Active : Status.Disabled; - } - } - - public class BackgroundFetchWorker : Worker - { - public BackgroundFetchWorker(global::Android.Content.Context context, WorkerParameters workerParameters) - : base(context, workerParameters) - { - } - - public override Result DoWork() - { - try - { - Task.Run(() => DoAsyncWork()).GetAwaiter().GetResult(); - return Result.InvokeSuccess(); - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine(ex); - return Result.InvokeRetry(); - } - } - - async Task DoAsyncWork() - { - if (await ExposureNotification.IsEnabledAsync()) - await ExposureNotification.UpdateKeysFromServer(); - } - } - - static partial class Utils - { - public static RiskLevel FromNative(this int riskLevel) => - riskLevel switch - { - AndroidRiskLevel.RiskLevelLowest => RiskLevel.Lowest, - AndroidRiskLevel.RiskLevelLow => RiskLevel.Low, - AndroidRiskLevel.RiskLevelLowMedium => RiskLevel.MediumLow, - AndroidRiskLevel.RiskLevelMedium => RiskLevel.Medium, - AndroidRiskLevel.RiskLevelMediumHigh => RiskLevel.MediumHigh, - AndroidRiskLevel.RiskLevelHigh => RiskLevel.High, - AndroidRiskLevel.RiskLevelVeryHigh => RiskLevel.VeryHigh, - AndroidRiskLevel.RiskLevelHighest => RiskLevel.Highest, - _ => AndroidRiskLevel.RiskLevelInvalid, - }; - - public static int ToNative(this RiskLevel riskLevel) => - riskLevel switch - { - RiskLevel.Lowest => AndroidRiskLevel.RiskLevelLowest, - RiskLevel.Low => AndroidRiskLevel.RiskLevelLow, - RiskLevel.MediumLow => AndroidRiskLevel.RiskLevelLowMedium, - RiskLevel.Medium => AndroidRiskLevel.RiskLevelMedium, - RiskLevel.MediumHigh => AndroidRiskLevel.RiskLevelMediumHigh, - RiskLevel.High => AndroidRiskLevel.RiskLevelHigh, - RiskLevel.VeryHigh => AndroidRiskLevel.RiskLevelVeryHigh, - RiskLevel.Highest => AndroidRiskLevel.RiskLevelHighest, - _ => AndroidRiskLevel.RiskLevelInvalid, - }; - } + public static partial class ExposureNotification + { + static IExposureNotificationClient instance; + + static IExposureNotificationClient Instance + => instance ??= Nearby.GetExposureNotificationClient(Application.Context); + + static async Task GetConfigurationAsync() + { + var c = await Handler.GetConfigurationAsync(); + + return new ExposureConfiguration.ExposureConfigurationBuilder() + .SetAttenuationScores(c.AttenuationScores) + .SetDurationScores(c.DurationScores) + .SetDaysSinceLastExposureScores(c.DaysSinceLastExposureScores) + .SetTransmissionRiskScores(c.TransmissionRiskScores) + .SetAttenuationWeight(c.AttenuationWeight) + .SetDaysSinceLastExposureWeight(c.DaysSinceLastExposureWeight) + .SetDurationWeight(c.DurationWeight) + .SetTransmissionRiskWeight(c.TransmissionWeight) + .SetMinimumRiskScore(c.MinimumRiskScore) + .SetDurationAtAttenuationThresholds(c.DurationAtAttenuationThresholds) + .Build(); + } + + const int requestCodeStartExposureNotification = 1111; + const int requestCodeGetTempExposureKeyHistory = 2222; + + static TaskCompletionSource tcsResolveConnection; + + public static void OnActivityResult(int requestCode, Result resultCode, global::Android.Content.Intent data) + { + if (requestCode == requestCodeStartExposureNotification || requestCode == requestCodeGetTempExposureKeyHistory) + { + if (resultCode == Result.Ok) + tcsResolveConnection?.TrySetResult(null); + else + tcsResolveConnection.TrySetException(new AccessDeniedException("Failed to resolve Exposure Notifications API")); + } + } + + static async Task ResolveApi(int requestCode, Func> apiCall) + { + try + { + return await apiCall(); + } + catch (ApiException apiEx) + { + if (apiEx.StatusCode == CommonStatusCodes.ResolutionRequired) // Resolution required + { + tcsResolveConnection = new TaskCompletionSource(); + + // Start the resolution + apiEx.Status.StartResolutionForResult(Essentials.Platform.CurrentActivity, requestCode); + + // Wait for the activity result to be called + await tcsResolveConnection.Task; + + // Try the original api call again + return await apiCall(); + } + } + + return default; + } + + static void PlatformInit() + { + _ = ScheduleFetchAsync(); + } + + static Task PlatformStart() + => ResolveApi(requestCodeStartExposureNotification, async () => + { + await Instance.StartAsync(); + return default; + }); + + static Task PlatformStop() + => ResolveApi(requestCodeStartExposureNotification, async () => + { + await Instance.StopAsync(); + return default; + }); + + static Task PlatformIsEnabled() + => ResolveApi(requestCodeStartExposureNotification, () => + Instance.IsEnabledAsync()); + + public static void ConfigureBackgroundWorkRequest(TimeSpan repeatInterval, Action requestBuilder) + { + if (requestBuilder == null) + throw new ArgumentNullException(nameof(requestBuilder)); + if (repeatInterval == null) + throw new ArgumentNullException(nameof(repeatInterval)); + + bgRequestBuilder = requestBuilder; + bgRepeatInterval = repeatInterval; + } + + static Action bgRequestBuilder = b => + b.SetConstraints(new Constraints.Builder() + .SetRequiresBatteryNotLow(true) + .SetRequiresDeviceIdle(true) + .SetRequiredNetworkType(NetworkType.Connected) + .Build()); + + static TimeSpan bgRepeatInterval = TimeSpan.FromHours(6); + + static Task PlatformScheduleFetch() + { + var workManager = WorkManager.GetInstance(Essentials.Platform.AppContext); + + var workRequestBuilder = new PeriodicWorkRequest.Builder( + typeof(BackgroundFetchWorker), + bgRepeatInterval); + + bgRequestBuilder.Invoke(workRequestBuilder); + + var workRequest = workRequestBuilder.Build(); + + workManager.EnqueueUniquePeriodicWork("exposurenotification", + ExistingPeriodicWorkPolicy.Replace, + workRequest); + + return Task.CompletedTask; + } + + // Tells the local API when new diagnosis keys have been obtained from the server + static async Task PlatformDetectExposuresAsync(IEnumerable keyFiles, System.Threading.CancellationToken cancellationToken) + { + var config = await GetConfigurationAsync(); + + await Instance.ProvideDiagnosisKeysAsync( + keyFiles.Select(f => new Java.IO.File(f)).ToList(), + config, + Guid.NewGuid().ToString()); + } + + static Task> PlatformGetTemporaryExposureKeys() + => ResolveApi(requestCodeGetTempExposureKeyHistory, async () => + { + var exposureKeyHistory = await Instance.GetTemporaryExposureKeyHistoryAsync(); + + return exposureKeyHistory.Select(k => + new TemporaryExposureKey( + k.GetKeyData(), + k.RollingStartIntervalNumber, + TimeSpan.FromMinutes(k.RollingPeriod * 10), + k.TransmissionRiskLevel.FromNative())); + }); + + internal static async Task> PlatformGetExposureInformationAsync(string token) + { + var exposures = await Instance.GetExposureInformationAsync(token); + var info = exposures.Select(d => new ExposureInfo( + DateTimeOffset.UnixEpoch.AddMilliseconds(d.DateMillisSinceEpoch).UtcDateTime, + TimeSpan.FromMinutes(d.DurationMinutes), + d.AttenuationValue, + d.TotalRiskScore, + d.TransmissionRiskLevel.FromNative())); + return info; + } + + internal static async Task PlatformGetExposureSummaryAsync(string token) + { + var summary = await Instance.GetExposureSummaryAsync(token); + + // TODO: Reevaluate byte usage here + return new ExposureDetectionSummary( + summary.DaysSinceLastExposure, + (ulong)summary.MatchedKeyCount, + summary.MaximumRiskScore, + summary.GetAttenuationDurationsInMinutes() + .Select(a => TimeSpan.FromMinutes(a)).ToArray(), + summary.SummationRiskScore); + } + + static async Task PlatformGetStatusAsync() + { + var bt = BluetoothAdapter.DefaultAdapter; + + if (bt == null || !bt.IsEnabled) + return Status.BluetoothOff; + + var status = await Instance.IsEnabledAsync(); + + return status ? Status.Active : Status.Disabled; + } + } + + public class BackgroundFetchWorker : Worker + { + public BackgroundFetchWorker(global::Android.Content.Context context, WorkerParameters workerParameters) + : base(context, workerParameters) + { + } + + public override Result DoWork() + { + try + { + Task.Run(() => DoAsyncWork()).GetAwaiter().GetResult(); + return Result.InvokeSuccess(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex); + return Result.InvokeRetry(); + } + } + + async Task DoAsyncWork() + { + if (await ExposureNotification.IsEnabledAsync()) + await ExposureNotification.UpdateKeysFromServer(); + } + } + + static partial class Utils + { + public static RiskLevel FromNative(this int riskLevel) => + riskLevel switch + { + AndroidRiskLevel.RiskLevelLowest => RiskLevel.Lowest, + AndroidRiskLevel.RiskLevelLow => RiskLevel.Low, + AndroidRiskLevel.RiskLevelLowMedium => RiskLevel.MediumLow, + AndroidRiskLevel.RiskLevelMedium => RiskLevel.Medium, + AndroidRiskLevel.RiskLevelMediumHigh => RiskLevel.MediumHigh, + AndroidRiskLevel.RiskLevelHigh => RiskLevel.High, + AndroidRiskLevel.RiskLevelVeryHigh => RiskLevel.VeryHigh, + AndroidRiskLevel.RiskLevelHighest => RiskLevel.Highest, + _ => AndroidRiskLevel.RiskLevelInvalid, + }; + + public static int ToNative(this RiskLevel riskLevel) => + riskLevel switch + { + RiskLevel.Lowest => AndroidRiskLevel.RiskLevelLowest, + RiskLevel.Low => AndroidRiskLevel.RiskLevelLow, + RiskLevel.MediumLow => AndroidRiskLevel.RiskLevelLowMedium, + RiskLevel.Medium => AndroidRiskLevel.RiskLevelMedium, + RiskLevel.MediumHigh => AndroidRiskLevel.RiskLevelMediumHigh, + RiskLevel.High => AndroidRiskLevel.RiskLevelHigh, + RiskLevel.VeryHigh => AndroidRiskLevel.RiskLevelVeryHigh, + RiskLevel.Highest => AndroidRiskLevel.RiskLevelHighest, + _ => AndroidRiskLevel.RiskLevelInvalid, + }; + } } diff --git a/Covid19Radar/Xamarin.ExposureNotification/ExposureNotification.customize.android.cs b/Covid19Radar/Xamarin.ExposureNotification/ExposureNotification.customize.android.cs deleted file mode 100644 index 17b20e077..000000000 --- a/Covid19Radar/Xamarin.ExposureNotification/ExposureNotification.customize.android.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Threading.Tasks; -using AndroidX.Work; - -namespace Xamarin.ExposureNotifications -{ - public static partial class ExposureNotification - { - private static readonly string[] OldWorkNames = { "exposurenotification" }; // Array of old work-name. - private static readonly string CurrentWorkName = "cocoaexposurenotification"; // Current work-name. (changed policy from `replace` to `keep`) - - // Schedule background work (Customization by COCOA) - static Task PlatformScheduleFetch() - { - CancelOldWork(); - - var workRequest = CreatePeriodicWorkRequest(); - EnqueueUniquePeriodicWork(workRequest); - - return Task.CompletedTask; - } - - private static void CancelOldWork() - { - var workManager = WorkManager.GetInstance(Essentials.Platform.AppContext); - foreach (var oldWorkName in OldWorkNames) - { - workManager.CancelUniqueWork(oldWorkName); - } - } - - private static PeriodicWorkRequest CreatePeriodicWorkRequest() - { - var workRequestBuilder = new PeriodicWorkRequest.Builder( - typeof(BackgroundFetchWorker), - bgRepeatInterval); - bgRequestBuilder.Invoke(workRequestBuilder); - return workRequestBuilder.Build(); - } - - private static void EnqueueUniquePeriodicWork(PeriodicWorkRequest workRequest) - { - var workManager = WorkManager.GetInstance(Essentials.Platform.AppContext); - workManager.EnqueueUniquePeriodicWork(CurrentWorkName, - ExistingPeriodicWorkPolicy.Keep, - workRequest); - } - } -}