// this will be called when they keys need to be collected from the server public async Task FetchExposureKeysFromServerAsync(ITemporaryExposureKeyBatches batches) { // This is "default" by default var region = LocalStateManager.Instance.Region ?? DefaultRegion; var checkForMore = true; do { try { // Find next batch number var batchNumber = LocalStateManager.Instance.ServerBatchNumber + 1; // Build the blob storage url for the given batch file we are on next var url = $"{apiUrlBlobStorageBase}/{blobStorageContainerNamePrefix}{region}/{batchNumber}.dat"; var response = await http.GetAsync(url); // If we get a 404, there are no newer batch files available to download if (response.StatusCode == System.Net.HttpStatusCode.NotFound) { checkForMore = false; break; } response.EnsureSuccessStatusCode(); // Skip batch files which are older than 14 days if (response.Content.Headers.LastModified.HasValue) { if (response.Content.Headers.LastModified < DateTimeOffset.UtcNow.AddDays(-14)) { LocalStateManager.Instance.ServerBatchNumber = batchNumber; LocalStateManager.Save(); checkForMore = true; continue; } } // Read the batch file stream using var responseStream = await response.Content.ReadAsStreamAsync(); // Parse into a Proto.File var batchFile = TemporaryExposureKeyBatch.Parser.ParseFrom(responseStream); // Submit to the batch processor await batches.AddBatchAsync(batchFile); // Update the number we are on LocalStateManager.Instance.ServerBatchNumber = batchNumber; LocalStateManager.Save(); } catch (Exception ex) { Console.WriteLine(ex); checkForMore = false; } } while (checkForMore); }
// this will be called when the user is submitting a diagnosis and the local keys need to go to the server public async Task UploadSelfExposureKeysToServerAsync(IEnumerable <TemporaryExposureKey> temporaryExposureKeys) { var pendingDiagnosis = LocalStateManager.Instance.PendingDiagnosis; if (pendingDiagnosis == null || string.IsNullOrEmpty(pendingDiagnosis.DiagnosisUid)) { throw new InvalidOperationException(); } try { var url = $"{apiUrlBase.TrimEnd('/')}/selfdiagnosis"; var json = JsonConvert.SerializeObject(new SelfDiagnosisSubmissionRequest { DiagnosisUid = pendingDiagnosis.DiagnosisUid, TestDate = pendingDiagnosis.DiagnosisDate.ToUnixTimeMilliseconds(), Keys = temporaryExposureKeys }); var http = new HttpClient(); var response = await http.PutAsync(url, new StringContent(json)); response.EnsureSuccessStatusCode(); // Update pending status pendingDiagnosis.Shared = true; LocalStateManager.Save(); } catch { throw; } }
public async Task UploadSelfExposureKeysToServer(IEnumerable <TemporaryExposureKey> temporaryExposureKeys) { var diagnosisUid = LocalStateManager.Instance.LatestDiagnosis.DiagnosisUid; if (string.IsNullOrEmpty(diagnosisUid)) { throw new InvalidOperationException(); } try { var url = $"{apiUrlBase.TrimEnd('/')}/selfdiagnosis"; var json = JsonConvert.SerializeObject(new SelfDiagnosisSubmissionRequest { DiagnosisUid = diagnosisUid, Keys = temporaryExposureKeys }); var http = new HttpClient(); var response = await http.PutAsync(url, new StringContent(json)); response.EnsureSuccessStatusCode(); LocalStateManager.Instance.LatestDiagnosis.Shared = true; LocalStateManager.Save(); } catch { throw; } }
public async Task FetchExposureKeysFromServer(Func <IEnumerable <TemporaryExposureKey>, Task> addKeys) { var latestKeysResponseIndex = LocalStateManager.Instance.LatestKeysResponseIndex; var take = 1024; var skip = 0; var checkForMore = false; do { // Get the newest date we have keys from and request since then // or if no date stored, only return as much as the past 14 days of keys var url = $"{apiUrlBase.TrimEnd('/')}/keys?since={latestKeysResponseIndex}&skip={skip}&take={take}"; var response = await http.GetAsync(url); response.EnsureSuccessStatusCode(); var responseData = await response.Content.ReadAsStringAsync(); if (string.IsNullOrEmpty(responseData)) { break; } // Response contains the timestamp in seconds since epoch, and the list of keys var keys = JsonConvert.DeserializeObject <KeysResponse>(responseData); var numKeys = keys?.Keys?.Count() ?? 0; // See if keys were returned on this call if (numKeys > 0) { // Call the callback with the batch of keys to add await addKeys(keys.Keys); var newLatestKeysResponseIndex = keys.Latest; if (newLatestKeysResponseIndex > LocalStateManager.Instance.LatestKeysResponseIndex) { LocalStateManager.Instance.LatestKeysResponseIndex = newLatestKeysResponseIndex; LocalStateManager.Save(); } // Increment our skip starting point for the next batch skip += take; } // If we got back more or the same amount of our requested take, there may be // more left on the server to request again checkForMore = numKeys >= take; } while (checkForMore); }
public async Task ExposureDetected(ExposureDetectionSummary summary, Func <Task <IEnumerable <ExposureInfo> > > getDetailsFunc) { LocalStateManager.Instance.ExposureSummary = summary; var details = await getDetailsFunc(); LocalStateManager.Instance.ExposureInformation.AddRange(details); LocalStateManager.Save(); MessagingCenter.Instance.Send(this, "exposure_info_changed"); // TODO: Save this info and alert the user // Pop up a local notification }
// this will be called when a potential exposure has been detected public async Task ExposureDetectedAsync(ExposureDetectionSummary summary, IEnumerable <ExposureInfo> exposureInfo) { LocalStateManager.Instance.ExposureSummary = summary; // Add these on main thread in case the UI is visible so it can update await Device.InvokeOnMainThreadAsync(() => { foreach (var i in exposureInfo) { LocalStateManager.Instance.ExposureInformation.Add(i); } }); LocalStateManager.Save(); var notification = new NotificationRequest { NotificationId = 100, Title = "Possible COVID-19 Exposure", Description = "It is possible you have been exposed to someone who was a confirmed diagnosis of COVID-19. Tap for more details." }; NotificationCenter.Current.Show(notification); }