mirror of
https://github.com/AU-COVIDSafe/mobile-ios.git
synced 2025-04-26 16:35:21 +00:00
COVIDSafe code from version 2.6 (#51)
This commit is contained in:
parent
195798ddd5
commit
4d98b6c5e4
43 changed files with 910 additions and 144 deletions
|
@ -16,10 +16,15 @@ class AuthenticationAPI: CovidSafeAuthenticatedAPI {
|
|||
return
|
||||
}
|
||||
|
||||
guard let authHeaders = try? authenticatedHeaders() else {
|
||||
completion(nil, .RequestError)
|
||||
return
|
||||
}
|
||||
|
||||
CovidNetworking.shared.session.request("\(apiHost)/issueInitialRefreshToken",
|
||||
method: .post,
|
||||
encoding: JSONEncoding.default,
|
||||
headers: authenticatedHeaders
|
||||
headers: authHeaders
|
||||
).validate().responseDecodable(of: ChallengeResponse.self) { (response) in
|
||||
switch response.result {
|
||||
case .success:
|
||||
|
@ -55,7 +60,7 @@ class AuthenticationAPI: CovidSafeAuthenticatedAPI {
|
|||
return
|
||||
}
|
||||
|
||||
let keychain = KeychainSwift()
|
||||
let keychain = KeychainSwift.shared
|
||||
|
||||
guard let token = keychain.get("JWT_TOKEN"),
|
||||
let refreshToken = keychain.get("REFRESH_TOKEN"),
|
||||
|
@ -105,7 +110,7 @@ class AuthenticationAPI: CovidSafeAuthenticatedAPI {
|
|||
}
|
||||
|
||||
static func issueTokensAPI(completion: @escaping (ChallengeResponse?, CovidSafeAPIError?) -> Void) {
|
||||
let keychain = KeychainSwift()
|
||||
let keychain = KeychainSwift.shared
|
||||
|
||||
// block api call only if refresh token exists, if it doesn't it means the app should get it for the first time
|
||||
if UserDefaults.standard.bool(forKey: "ReauthenticationNeededKey") && keychain.get("REFRESH_TOKEN") != nil {
|
||||
|
@ -132,13 +137,20 @@ class AuthenticationAPI: CovidSafeAuthenticatedAPI {
|
|||
completion(response, nil)
|
||||
}
|
||||
} else {
|
||||
let refreshTokenBeforeRefresh = keychain.get("REFRESH_TOKEN")
|
||||
AuthenticationAPI.issueJWTTokenAPI { (response, error) in
|
||||
|
||||
guard let jwt = response?.token,
|
||||
let refresh = response?.refreshToken,
|
||||
error == nil else {
|
||||
|
||||
// set corrupted
|
||||
// if the token in the system before the call is different than the one in keychain, this is a concurrency issue, the token was refresh already and that is why this failed. We should not start re-authentication
|
||||
guard refreshTokenBeforeRefresh == keychain.get("REFRESH_TOKEN") else {
|
||||
completion(response, .TokenAlreadyRefreshedError)
|
||||
return
|
||||
}
|
||||
|
||||
// set corrupted only when it has not been changed
|
||||
UserDefaults.standard.set(true, forKey: "ReauthenticationNeededKey")
|
||||
completion(response, .TokenExpiredError)
|
||||
return
|
||||
|
|
|
@ -20,11 +20,17 @@ class ChangePostcodeAPI: CovidSafeAuthenticatedAPI {
|
|||
let params = [
|
||||
"postcode": newPostcode,
|
||||
]
|
||||
|
||||
guard let authHeaders = try? authenticatedHeaders() else {
|
||||
completion(.RequestError)
|
||||
return
|
||||
}
|
||||
|
||||
CovidNetworking.shared.session.request("\(apiHost)/device",
|
||||
method: .post,
|
||||
parameters: params,
|
||||
encoding: JSONEncoding.default,
|
||||
headers: authenticatedHeaders,
|
||||
headers: authHeaders,
|
||||
interceptor: CovidRequestRetrier(retries:3)).validate().responseDecodable(of: DeviceResponse.self) { (response) in
|
||||
switch response.result {
|
||||
case .success:
|
||||
|
|
|
@ -56,6 +56,8 @@ enum CovidSafeAPIError: Error {
|
|||
case ResponseError
|
||||
case ServerError
|
||||
case TokenExpiredError
|
||||
case TokenAlreadyRefreshedError
|
||||
case MaxRegistrationError
|
||||
case UnknownError
|
||||
}
|
||||
|
||||
|
@ -63,18 +65,16 @@ class CovidSafeAuthenticatedAPI {
|
|||
|
||||
static var isBusy = false
|
||||
|
||||
static var authenticatedHeaders: HTTPHeaders {
|
||||
get {
|
||||
let keychain = KeychainSwift()
|
||||
|
||||
guard let token = keychain.get("JWT_TOKEN") else {
|
||||
return []
|
||||
}
|
||||
let headers: HTTPHeaders = [
|
||||
"Authorization": "Bearer \(token)"
|
||||
]
|
||||
return headers
|
||||
static func authenticatedHeaders() throws -> HTTPHeaders {
|
||||
let keychain = KeychainSwift.shared
|
||||
|
||||
guard let token = keychain.get("JWT_TOKEN") else {
|
||||
throw CovidSafeAPIError.RequestError
|
||||
}
|
||||
let headers: HTTPHeaders = [
|
||||
"Authorization": "Bearer \(token)"
|
||||
]
|
||||
return headers
|
||||
}
|
||||
|
||||
static func processUnauthorizedError(_ data: Data) -> CovidSafeAPIError {
|
||||
|
|
|
@ -20,8 +20,12 @@ final class CovidRequestRetrier: Alamofire.RequestInterceptor {
|
|||
|
||||
func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
|
||||
var urlRequest = urlRequest
|
||||
let keychain = KeychainSwift()
|
||||
let keychain = KeychainSwift.shared
|
||||
let refreshExists = keychain.get("REFRESH_TOKEN") != nil
|
||||
|
||||
// turn off geolock error
|
||||
UserDefaults.standard.setValue(false, forKey: showGeolockErrorKey)
|
||||
|
||||
// prevent authenticated api calls if the re-registration flow has been started
|
||||
if UserDefaults.standard.bool(forKey: "ReauthenticationNeededKey") &&
|
||||
refreshExists {
|
||||
|
@ -81,13 +85,19 @@ final class CovidRequestRetrier: Alamofire.RequestInterceptor {
|
|||
return completion(.retryWithDelay(1.0))
|
||||
}
|
||||
|
||||
if let serverHeader = response.headers.first(where: { $0.name == "Server" }),
|
||||
response.statusCode == 403 && serverHeader.value == "CloudFront" {
|
||||
UserDefaults.standard.setValue(true, forKey: showGeolockErrorKey)
|
||||
return completion(.doNotRetryWithError(error))
|
||||
}
|
||||
|
||||
if !triedRefresh &&
|
||||
(response.statusCode == 403 || response.statusCode == 401) {
|
||||
(response.statusCode == 401 || response.statusCode == 403) {
|
||||
triedRefresh = true
|
||||
retriesExecuted += 1
|
||||
AuthenticationAPI.issueTokensAPI { (response, authError) in
|
||||
// this will update the tokens automatically
|
||||
guard let respError = authError, respError == .TokenExpiredError else {
|
||||
if let respError = authError, respError == .TokenExpiredError {
|
||||
completion(.doNotRetryWithError(error))
|
||||
return
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ class GetTempIdAPI: CovidSafeAuthenticatedAPI {
|
|||
"version" : apiVersion
|
||||
]
|
||||
|
||||
guard authenticatedHeaders.count > 0 else {
|
||||
guard let authHeaders = try? authenticatedHeaders(), authHeaders.count > 0 else {
|
||||
completion(nil, nil, nil, .TokenExpiredError)
|
||||
return
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ class GetTempIdAPI: CovidSafeAuthenticatedAPI {
|
|||
CovidNetworking.shared.session.request("\(apiHost)/getTempId",
|
||||
method: .get,
|
||||
parameters: params,
|
||||
headers: authenticatedHeaders,
|
||||
headers: authHeaders,
|
||||
interceptor: CovidRequestRetrier(retries: 3)).validate().responseDecodable(of: TempIdResponse.self) { (response) in
|
||||
switch response.result {
|
||||
case .success:
|
||||
|
|
|
@ -65,7 +65,7 @@ class MessageAPI: CovidSafeAuthenticatedAPI {
|
|||
var shouldGetMessages = true
|
||||
|
||||
let calendar = NSCalendar.current
|
||||
let currentDate = calendar.startOfDay(for: Date())
|
||||
let currentDate = Date()
|
||||
|
||||
// if the current version is newer than the last version checked, allow messages call
|
||||
if let currVersionStr = Bundle.main.version, let currVersion = Int(currVersionStr), currVersion > versionChecked {
|
||||
|
@ -77,7 +77,7 @@ class MessageAPI: CovidSafeAuthenticatedAPI {
|
|||
let components = calendar.dateComponents([.hour], from: lastCheckedDate, to: currentDate)
|
||||
|
||||
if let numHours = components.hour {
|
||||
shouldGetMessages = numHours > 4
|
||||
shouldGetMessages = numHours >= 4
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,10 +109,15 @@ class MessageAPI: CovidSafeAuthenticatedAPI {
|
|||
|
||||
isBusy = true
|
||||
|
||||
guard let authHeaders = try? authenticatedHeaders() else {
|
||||
completion(nil, .RequestError)
|
||||
return
|
||||
}
|
||||
|
||||
CovidNetworking.shared.session.request("\(apiHost)/messages",
|
||||
method: .get,
|
||||
parameters: params,
|
||||
headers: authenticatedHeaders,
|
||||
headers: authHeaders,
|
||||
interceptor: CovidRequestRetrier(retries: 3)
|
||||
).validate().responseDecodable(of: MessageResponse.self) { (response) in
|
||||
switch response.result {
|
||||
|
@ -120,7 +125,7 @@ class MessageAPI: CovidSafeAuthenticatedAPI {
|
|||
guard let messageResponse = response.value else { return }
|
||||
|
||||
// save successful timestamp
|
||||
let minutesToDefer = Int.random(in: 0..<10)
|
||||
let minutesToDefer = Int.random(in: 0..<30)
|
||||
let calendar = NSCalendar.current
|
||||
let currentDate = Date()
|
||||
if let deferredDate = calendar.date(byAdding: .minute, value: minutesToDefer, to: currentDate) {
|
||||
|
|
|
@ -11,7 +11,7 @@ import Alamofire
|
|||
class PhoneValidationAPI {
|
||||
|
||||
static func verifyPhoneNumber(regInfo: RegistrationRequest,
|
||||
completion: @escaping (String?, Swift.Error?) -> Void) {
|
||||
completion: @escaping (String?, CovidSafeAPIError?) -> Void) {
|
||||
|
||||
guard let apiHost = PlistHelper.getvalueFromInfoPlist(withKey: "API_Host", plistName: "CovidSafe-config") else {
|
||||
return
|
||||
|
@ -33,8 +33,22 @@ class PhoneValidationAPI {
|
|||
case .success:
|
||||
guard let authResponse = response.value else { return }
|
||||
completion(authResponse.session, nil)
|
||||
case let .failure(error):
|
||||
completion(nil, error)
|
||||
case .failure(_):
|
||||
var apiError = CovidSafeAPIError.RequestError
|
||||
|
||||
if let respData = response.data {
|
||||
do {
|
||||
let errorResponse = try JSONDecoder().decode(CovidSafeErrorResponse.self, from: respData)
|
||||
if errorResponse.message == "MaxRegistrationsReached" {
|
||||
apiError = .MaxRegistrationError
|
||||
}
|
||||
} catch {
|
||||
// unable to parse response
|
||||
apiError = .ResponseError
|
||||
}
|
||||
}
|
||||
completion(nil, apiError)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,10 +24,15 @@ class RestrictionsAPI: CovidSafeAuthenticatedAPI {
|
|||
|
||||
let params = ["state": "\(forState.rawValue.lowercased())"]
|
||||
|
||||
guard let authHeaders = try? authenticatedHeaders() else {
|
||||
completion(nil, .RequestError)
|
||||
return
|
||||
}
|
||||
|
||||
CovidNetworking.shared.session.request("\(apiHost)/restrictions",
|
||||
method: .get,
|
||||
parameters: params,
|
||||
headers: authenticatedHeaders,
|
||||
headers: authHeaders,
|
||||
interceptor: CovidRequestRetrier(retries: 3)
|
||||
).validate().responseDecodable(of: StateRestriction.self) { (response) in
|
||||
switch response.result {
|
||||
|
|
|
@ -20,10 +20,15 @@ class StatisticsAPI: CovidSafeAuthenticatedAPI {
|
|||
|
||||
let parameters = ["state" : "\(forState.rawValue)"]
|
||||
|
||||
guard let authHeaders = try? authenticatedHeaders() else {
|
||||
completion(nil, .RequestError)
|
||||
return
|
||||
}
|
||||
|
||||
CovidNetworking.shared.session.request("\(apiHost)/v2/statistics",
|
||||
method: .get,
|
||||
parameters: parameters,
|
||||
headers: authenticatedHeaders,
|
||||
headers: authHeaders,
|
||||
interceptor: CovidRequestRetrier(retries: 3)
|
||||
).validate().responseDecodable(of: StatisticsResponse.self) { (response) in
|
||||
switch response.result {
|
||||
|
|
|
@ -21,7 +21,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
setupCoredataDir()
|
||||
let firstRun = UserDefaults.standard.bool(forKey: "HasBeenLaunched")
|
||||
if( !firstRun ) {
|
||||
let keychain = KeychainSwift()
|
||||
let keychain = KeychainSwift.shared
|
||||
keychain.clear()
|
||||
UserDefaults.standard.set(true, forKey: "HasBeenLaunched")
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
<outlet property="covidStatisticsContainer" destination="eZl-C5-gSv" id="37c-CV-VgS"/>
|
||||
<outlet property="covidStatisticsSection" destination="Aop-Ae-hRv" id="Qpd-Hv-uA8"/>
|
||||
<outlet property="inactiveAppSectionView" destination="784-Jf-kOX" id="J3m-Pu-697"/>
|
||||
<outlet property="inactiveGenericError" destination="5Pd-c8-CYx" id="Fez-q4-Oc5"/>
|
||||
<outlet property="inactiveSettingsContent" destination="AUW-C2-ven" id="NNP-o9-zkK"/>
|
||||
<outlet property="inactiveTokenExpiredView" destination="nxM-ji-ttb" id="vVC-KW-yek"/>
|
||||
<outlet property="locationPermissionsView" destination="u4f-uR-ri3" id="70q-Jr-1cc"/>
|
||||
|
@ -127,19 +128,19 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<stackView opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="h36-8c-K2n">
|
||||
<rect key="frame" x="0.0" y="120" width="414" height="1940.5"/>
|
||||
<rect key="frame" x="0.0" y="120" width="414" height="2105"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="bRs-XW-qzv" userLabel="StatusView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="1136"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="1300.5"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="rcS-nL-IAO">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="1124"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="1288.5"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="784-Jf-kOX" userLabel="InactiveView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="696.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="861"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="1" translatesAutoresizingMaskIntoConstraints="NO" id="xti-6W-zko" userLabel="Inactive Stack View">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="696.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="861"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="4fe-SU-8Q6" userLabel="InActiveHeader">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="81"/>
|
||||
|
@ -246,7 +247,7 @@
|
|||
</subviews>
|
||||
<accessibility key="accessibilityConfiguration" label="Bluetooth permissions off">
|
||||
<accessibilityTraits key="traits" button="YES"/>
|
||||
<bool key="isElement" value="YES"/>
|
||||
<bool key="isElement" value="NO"/>
|
||||
</accessibility>
|
||||
<constraints>
|
||||
<constraint firstItem="reL-DQ-aFx" firstAttribute="leading" secondItem="nxM-ji-ttb" secondAttribute="leading" id="DNN-Va-LUA"/>
|
||||
|
@ -255,8 +256,69 @@
|
|||
<constraint firstAttribute="bottom" secondItem="reL-DQ-aFx" secondAttribute="bottom" id="qeE-Hu-OAb"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5Pd-c8-CYx" userLabel="Generic Error View">
|
||||
<rect key="frame" x="0.0" y="369" width="414" height="163.5"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="8Nu-a1-6fd" userLabel="Generic Error Bar">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="163.5"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Error connectiong to server" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="yw4-mY-Pvb">
|
||||
<rect key="frame" x="16" y="16" width="238.5" height="0.0"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" id="K73-MG-bAA"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle3"/>
|
||||
<color key="textColor" red="0.63921568630000003" green="0.098039215690000001" blue="0.098039215690000001" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Iw2-bN-Bjb">
|
||||
<rect key="frame" x="16" y="16" width="350" height="131.5"/>
|
||||
<accessibility key="accessibilityConfiguration">
|
||||
<bool key="isElement" value="NO"/>
|
||||
</accessibility>
|
||||
<string key="text">It looks like you may be accessing the internet from outside Australia. This sometimes happens when roaming on an international simcard. Please connect to an Australian network to continue.</string>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleCallout"/>
|
||||
<color key="textColor" red="0.63921568630000003" green="0.098039215690000001" blue="0.098039215690000001" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizationKey" value="geoblock_error_message"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<imageView hidden="YES" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="chevron-right-red" highlightedImage="redcross" translatesAutoresizingMaskIntoConstraints="NO" id="JZ8-95-6mR">
|
||||
<rect key="frame" x="374" y="16" width="24" height="24"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="24" id="5Dg-TP-jeV"/>
|
||||
<constraint firstAttribute="height" constant="24" id="QDY-8b-rGB"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="yw4-mY-Pvb" firstAttribute="top" secondItem="8Nu-a1-6fd" secondAttribute="top" constant="16" id="8lj-Mp-QPd"/>
|
||||
<constraint firstItem="Iw2-bN-Bjb" firstAttribute="top" secondItem="yw4-mY-Pvb" secondAttribute="bottom" id="BG1-vf-8aa"/>
|
||||
<constraint firstItem="yw4-mY-Pvb" firstAttribute="leading" secondItem="8Nu-a1-6fd" secondAttribute="leading" constant="16" id="FB0-Gh-26E"/>
|
||||
<constraint firstItem="Iw2-bN-Bjb" firstAttribute="leading" secondItem="8Nu-a1-6fd" secondAttribute="leading" constant="16" id="MsQ-XB-0Cp"/>
|
||||
<constraint firstItem="JZ8-95-6mR" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="yw4-mY-Pvb" secondAttribute="trailing" constant="8" id="N9H-1e-dzZ"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Iw2-bN-Bjb" secondAttribute="bottom" constant="16" id="SmG-4M-0Ir"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Iw2-bN-Bjb" secondAttribute="trailing" constant="48" id="YQ6-nh-9k7"/>
|
||||
<constraint firstAttribute="trailing" secondItem="JZ8-95-6mR" secondAttribute="trailing" constant="16" id="jSl-T3-bbB"/>
|
||||
<constraint firstItem="JZ8-95-6mR" firstAttribute="top" secondItem="8Nu-a1-6fd" secondAttribute="top" constant="16" id="toG-5o-Kri"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<accessibility key="accessibilityConfiguration">
|
||||
<accessibilityTraits key="traits" button="YES"/>
|
||||
<bool key="isElement" value="NO"/>
|
||||
</accessibility>
|
||||
<constraints>
|
||||
<constraint firstItem="8Nu-a1-6fd" firstAttribute="leading" secondItem="5Pd-c8-CYx" secondAttribute="leading" id="GvM-yl-CrV"/>
|
||||
<constraint firstAttribute="bottom" secondItem="8Nu-a1-6fd" secondAttribute="bottom" id="gZR-KX-tky"/>
|
||||
<constraint firstItem="8Nu-a1-6fd" firstAttribute="top" secondItem="5Pd-c8-CYx" secondAttribute="top" id="gl7-zv-NsS"/>
|
||||
<constraint firstAttribute="trailing" secondItem="8Nu-a1-6fd" secondAttribute="trailing" id="tnp-QS-aoM"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="a5D-xk-n0n" userLabel="Bluetooth Permission Off Section">
|
||||
<rect key="frame" x="0.0" y="369" width="414" height="80.5"/>
|
||||
<rect key="frame" x="0.0" y="533.5" width="414" height="80.5"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ZoL-pC-0bR" userLabel="Bluetooth Permission Off Bar">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="80.5"/>
|
||||
|
@ -327,7 +389,7 @@
|
|||
</connections>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ers-f9-BFH" userLabel="Bluetooth Status Off Section">
|
||||
<rect key="frame" x="0.0" y="450.5" width="414" height="101.5"/>
|
||||
<rect key="frame" x="0.0" y="615" width="414" height="101.5"/>
|
||||
<subviews>
|
||||
<view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="p5d-dk-foR" userLabel="Bluetooth Status Bar Off">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="101.5"/>
|
||||
|
@ -394,7 +456,7 @@
|
|||
</connections>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" verticalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="u4f-uR-ri3" userLabel="Location Settings">
|
||||
<rect key="frame" x="0.0" y="553" width="414" height="143.5"/>
|
||||
<rect key="frame" x="0.0" y="717.5" width="414" height="143.5"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="aGh-Fm-LNa" userLabel="Location Off bar">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="142.5"/>
|
||||
|
@ -473,7 +535,7 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="m1D-93-7sF" userLabel="ActiveView">
|
||||
<rect key="frame" x="0.0" y="696.5" width="414" height="427.5"/>
|
||||
<rect key="frame" x="0.0" y="861" width="414" height="427.5"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="1" translatesAutoresizingMaskIntoConstraints="NO" id="981-Cd-Xm3" userLabel="Active Stack View">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="427.5"/>
|
||||
|
@ -592,7 +654,7 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Aop-Ae-hRv" userLabel="StatisticsViewSection">
|
||||
<rect key="frame" x="0.0" y="1136" width="414" height="124"/>
|
||||
<rect key="frame" x="0.0" y="1300.5" width="414" height="124"/>
|
||||
<subviews>
|
||||
<view clipsSubviews="YES" contentMode="scaleToFill" verticalHuggingPriority="1" translatesAutoresizingMaskIntoConstraints="NO" id="eZl-C5-gSv" userLabel="StatisticsView">
|
||||
<rect key="frame" x="0.0" y="12" width="414" height="100"/>
|
||||
|
@ -610,7 +672,7 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9cE-NC-A20" userLabel="Help">
|
||||
<rect key="frame" x="0.0" y="1260" width="414" height="153"/>
|
||||
<rect key="frame" x="0.0" y="1424.5" width="414" height="153"/>
|
||||
<subviews>
|
||||
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="co1-dK-1WU">
|
||||
<rect key="frame" x="0.0" y="12" width="414" height="129"/>
|
||||
|
@ -743,7 +805,7 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7cN-DY-lc3" userLabel="Change language">
|
||||
<rect key="frame" x="0.0" y="1413" width="414" height="153"/>
|
||||
<rect key="frame" x="0.0" y="1577.5" width="414" height="153"/>
|
||||
<subviews>
|
||||
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="bE9-gT-Hba">
|
||||
<rect key="frame" x="0.0" y="12" width="414" height="129"/>
|
||||
|
@ -876,7 +938,7 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="CGS-rw-uZS" userLabel="PrivacyPolicy">
|
||||
<rect key="frame" x="0.0" y="1566" width="414" height="153"/>
|
||||
<rect key="frame" x="0.0" y="1730.5" width="414" height="153"/>
|
||||
<subviews>
|
||||
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="BBB-zD-Non">
|
||||
<rect key="frame" x="0.0" y="12" width="414" height="129"/>
|
||||
|
@ -885,7 +947,7 @@
|
|||
<rect key="frame" x="0.0" y="0.0" width="414" height="129"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Privacy Policy" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="mFo-iF-cX3">
|
||||
<rect key="frame" x="72" y="16" width="302" height="24"/>
|
||||
<rect key="frame" x="72" y="16" width="302" height="77.5"/>
|
||||
<accessibility key="accessibilityConfiguration">
|
||||
<accessibilityTraits key="traits" none="YES"/>
|
||||
<bool key="isElement" value="NO"/>
|
||||
|
@ -908,7 +970,7 @@
|
|||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Privacy Policy for COVIDSafe Application" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gF9-eh-ER2">
|
||||
<rect key="frame" x="72" y="40" width="302" height="73"/>
|
||||
<rect key="frame" x="72" y="93.5" width="302" height="19.5"/>
|
||||
<accessibility key="accessibilityConfiguration">
|
||||
<accessibilityTraits key="traits" none="YES"/>
|
||||
<bool key="isElement" value="NO"/>
|
||||
|
@ -1009,7 +1071,7 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vs9-rS-UOM" userLabel="Share CovidSafe">
|
||||
<rect key="frame" x="0.0" y="1719" width="414" height="56"/>
|
||||
<rect key="frame" x="0.0" y="1883.5" width="414" height="56"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="RCa-zU-3Vo">
|
||||
<rect key="frame" x="0.0" y="12" width="414" height="32"/>
|
||||
|
@ -1120,7 +1182,7 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="JSe-D6-hyV" userLabel="Upload Data">
|
||||
<rect key="frame" x="0.0" y="1719" width="414" height="165.5"/>
|
||||
<rect key="frame" x="0.0" y="1883.5" width="414" height="165.5"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="8pS-Df-p0U">
|
||||
<rect key="frame" x="0.0" y="12" width="414" height="141.5"/>
|
||||
|
@ -1240,7 +1302,7 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="eZ2-CQ-dtQ" userLabel="Version View">
|
||||
<rect key="frame" x="0.0" y="1884.5" width="414" height="56"/>
|
||||
<rect key="frame" x="0.0" y="2049" width="414" height="56"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Version number:" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="O1w-Sl-OIH" userLabel="Version">
|
||||
<rect key="frame" x="16" y="12" width="382" height="20"/>
|
||||
|
|
|
@ -4,15 +4,18 @@ import KeychainSwift
|
|||
import SafariServices
|
||||
import Reachability
|
||||
|
||||
let reauthenticationNeededKey = "ReauthenticationNeededKey"
|
||||
let showGeolockErrorKey = "showGeolockErrorKey"
|
||||
|
||||
class HomeViewController: UIViewController, HomeDelegate {
|
||||
private var observer: NSObjectProtocol?
|
||||
private let reauthenticationNeededKey = "ReauthenticationNeededKey"
|
||||
|
||||
@IBOutlet weak var bluetoothStatusOffView: UIView!
|
||||
@IBOutlet weak var bluetoothPermissionOffView: UIView!
|
||||
@IBOutlet weak var locationPermissionsView: UIView!
|
||||
@IBOutlet weak var inactiveSettingsContent: UIView!
|
||||
@IBOutlet weak var inactiveTokenExpiredView: UIView!
|
||||
@IBOutlet weak var inactiveGenericError: UIView!
|
||||
@IBOutlet weak var shareView: UIView!
|
||||
@IBOutlet weak var inactiveAppSectionView: UIView!
|
||||
@IBOutlet weak var activeAppSectionView: UIView!
|
||||
|
@ -42,8 +45,17 @@ class HomeViewController: UIViewController, HomeDelegate {
|
|||
|
||||
var allPermissionOn = true
|
||||
|
||||
var registrationNeeded: Bool {
|
||||
return UserDefaults.standard.bool(forKey: reauthenticationNeededKey)
|
||||
var showErrorToUser: Bool {
|
||||
return showRegistrationError ||
|
||||
showGenericError
|
||||
}
|
||||
|
||||
var showGenericError: Bool {
|
||||
return UserDefaults.standard.bool(forKey: showGeolockErrorKey)
|
||||
}
|
||||
|
||||
var showRegistrationError: Bool {
|
||||
return UserDefaults.standard.bool(forKey: reauthenticationNeededKey)
|
||||
}
|
||||
|
||||
var bluetoothStatusOn = true
|
||||
|
@ -222,7 +234,7 @@ class HomeViewController: UIViewController, HomeDelegate {
|
|||
|
||||
func updateJWTKeychainAccess() {
|
||||
let hasUpdatedKeychainAccess = UserDefaults.standard.bool(forKey: "HasUpdatedKeychainAccess")
|
||||
let keychain = KeychainSwift()
|
||||
let keychain = KeychainSwift.shared
|
||||
if (!hasUpdatedKeychainAccess) {
|
||||
if let jwt = keychain.get("JWT_TOKEN") {
|
||||
if (keychain.set(jwt, forKey: "JWT_TOKEN", withAccess: .accessibleAfterFirstUnlock)) {
|
||||
|
@ -272,6 +284,7 @@ class HomeViewController: UIViewController, HomeDelegate {
|
|||
self?.toggleShareView()
|
||||
self?.toggleStatisticsView()
|
||||
self?.toggleRegistrationNeededView()
|
||||
self?.toggleGenericErrorView()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -349,37 +362,41 @@ class HomeViewController: UIViewController, HomeDelegate {
|
|||
}
|
||||
|
||||
fileprivate func toggleUploadView() {
|
||||
toggleViewVisibility(view: self.uploadView, isVisible: !self.didUploadData && !self.registrationNeeded)
|
||||
toggleViewVisibility(view: self.uploadView, isVisible: !self.didUploadData && !self.showErrorToUser)
|
||||
}
|
||||
|
||||
fileprivate func toggleShareView() {
|
||||
toggleViewVisibility(view: shareView, isVisible: !registrationNeeded)
|
||||
toggleViewVisibility(view: shareView, isVisible: !showErrorToUser)
|
||||
}
|
||||
|
||||
fileprivate func toggleStatisticsView() {
|
||||
toggleViewVisibility(view: covidStatisticsSection, isVisible: !registrationNeeded)
|
||||
toggleViewVisibility(view: covidStatisticsSection, isVisible: !showErrorToUser)
|
||||
}
|
||||
|
||||
fileprivate func toggleHeaderView() {
|
||||
toggleViewVisibility(view: inactiveAppSectionView, isVisible: !self.allPermissionOn || registrationNeeded)
|
||||
toggleViewVisibility(view: inactiveSettingsContent, isVisible: !self.allPermissionOn && !registrationNeeded)
|
||||
toggleViewVisibility(view: activeAppSectionView, isVisible: self.allPermissionOn)
|
||||
toggleViewVisibility(view: inactiveAppSectionView, isVisible: !self.allPermissionOn || showErrorToUser)
|
||||
toggleViewVisibility(view: inactiveSettingsContent, isVisible: !self.allPermissionOn && !showErrorToUser)
|
||||
toggleViewVisibility(view: activeAppSectionView, isVisible: self.allPermissionOn && !showErrorToUser)
|
||||
}
|
||||
|
||||
fileprivate func toggleBluetoothStatusView() {
|
||||
toggleViewVisibility(view: bluetoothStatusOffView, isVisible: self.bluetoothPermissionOn && !self.bluetoothStatusOn && !registrationNeeded)
|
||||
toggleViewVisibility(view: bluetoothStatusOffView, isVisible: self.bluetoothPermissionOn && !self.bluetoothStatusOn && !showErrorToUser)
|
||||
}
|
||||
|
||||
fileprivate func toggleBluetoothPermissionStatusView() {
|
||||
toggleViewVisibility(view: bluetoothPermissionOffView, isVisible: !self.allPermissionOn && !self.bluetoothPermissionOn && !registrationNeeded)
|
||||
toggleViewVisibility(view: bluetoothPermissionOffView, isVisible: !self.allPermissionOn && !self.bluetoothPermissionOn && !showErrorToUser)
|
||||
}
|
||||
|
||||
fileprivate func toggleLocationPermissionStatusView() {
|
||||
toggleViewVisibility(view: locationPermissionsView, isVisible: !allPermissionOn && !locationPermissionOn && (bluetoothPermissionOn && bluetoothStatusOn) && !registrationNeeded)
|
||||
toggleViewVisibility(view: locationPermissionsView, isVisible: !allPermissionOn && !locationPermissionOn && (bluetoothPermissionOn && bluetoothStatusOn) && !showErrorToUser)
|
||||
}
|
||||
|
||||
fileprivate func toggleRegistrationNeededView() {
|
||||
toggleViewVisibility(view: inactiveTokenExpiredView, isVisible: registrationNeeded)
|
||||
toggleViewVisibility(view: inactiveTokenExpiredView, isVisible: showRegistrationError)
|
||||
}
|
||||
|
||||
fileprivate func toggleGenericErrorView() {
|
||||
toggleViewVisibility(view: inactiveGenericError, isVisible: showGenericError)
|
||||
}
|
||||
|
||||
func attemptTurnOnBluetooth() {
|
||||
|
|
|
@ -53,7 +53,7 @@ final class InfoViewController: UIViewController {
|
|||
silentNotificationsCountLabel.text = "\(UserDefaults.standard.integer(forKey: "debugSilentNotificationCount"))"
|
||||
apnTokenLabel.text = UserDefaults.standard.string(forKey: "deviceTokenForAPN")
|
||||
|
||||
let keychain = KeychainSwift()
|
||||
let keychain = KeychainSwift.shared
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateStyle = .short
|
||||
dateFormatter.timeStyle = .medium
|
||||
|
@ -92,7 +92,7 @@ final class InfoViewController: UIViewController {
|
|||
}
|
||||
|
||||
@IBAction func requestUploadOTP(_ sender: UIButton) {
|
||||
let keychain = KeychainSwift()
|
||||
let keychain = KeychainSwift.shared
|
||||
guard let jwt = keychain.get("JWT_TOKEN") else {
|
||||
DLog("Error trying to upload when not logged in")
|
||||
return
|
||||
|
@ -199,7 +199,7 @@ final class InfoViewController: UIViewController {
|
|||
}
|
||||
|
||||
@IBAction func setReauthenticationNeeded(_ sender: Any) {
|
||||
let keychain = KeychainSwift()
|
||||
let keychain = KeychainSwift.shared
|
||||
keychain.set("corruptedjwt", forKey: "JWT_TOKEN", withAccess: .accessibleAfterFirstUnlock)
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ class InitialScreenViewController: UIViewController, EncounterDBMigrationProgres
|
|||
var migrationStart: Date?
|
||||
var isKeychainAvailable = false
|
||||
var isDisplayTimeElapsed = false
|
||||
let keychain = KeychainSwift()
|
||||
let keychain = KeychainSwift.shared
|
||||
var giveupTimer: Timer?
|
||||
var initialDelayTimer: Timer?
|
||||
var migrationViewController: UIViewController?
|
||||
|
|
13
CovidSafe/KeychainSwift+Singleton.swift
Normal file
13
CovidSafe/KeychainSwift+Singleton.swift
Normal file
|
@ -0,0 +1,13 @@
|
|||
//
|
||||
// KeychainSwift+Singleton.swift
|
||||
// CovidSafe
|
||||
//
|
||||
// Copyright © 2021 Australian Government. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import KeychainSwift
|
||||
|
||||
extension KeychainSwift {
|
||||
static var shared = KeychainSwift()
|
||||
}
|
|
@ -240,7 +240,7 @@ class OTPViewController: UIViewController, RegistrationHandler {
|
|||
result(.WrongOTP)
|
||||
return
|
||||
}
|
||||
let keychain = KeychainSwift()
|
||||
let keychain = KeychainSwift.shared
|
||||
keychain.set(tokenToStore, forKey: "JWT_TOKEN", withAccess: .accessibleAfterFirstUnlock)
|
||||
keychain.set(refreshToken, forKey: "REFRESH_TOKEN", withAccess: .accessibleAfterFirstUnlock)
|
||||
UserDefaults.standard.set(true, forKey: "HasUpdatedKeychainAccess")
|
||||
|
|
|
@ -135,12 +135,34 @@ class PhoneNumberViewController: UIViewController, UITextFieldDelegate, Registra
|
|||
self?.activityIndicator.stopAnimating()
|
||||
self?.getOTPButton.isEnabled = true
|
||||
if let error = error {
|
||||
|
||||
let alertMessage = error == .MaxRegistrationError ? "max_registrations".localizedString() : "PhoneVerificationErrorMessage".localizedString()
|
||||
|
||||
let errorAlert = UIAlertController(title: "PhoneVerificationErrorTitle".localizedString(),
|
||||
message: "PhoneVerificationErrorMessage".localizedString(),
|
||||
message: alertMessage,
|
||||
preferredStyle: .alert)
|
||||
errorAlert.addAction(UIAlertAction(title: "global_OK".localizedString(), style: .default, handler: { _ in
|
||||
DLog("Unable to verify phone number")
|
||||
}))
|
||||
if error == .MaxRegistrationError {
|
||||
errorAlert.addAction(UIAlertAction(title: "max_registrations_button1".localizedString(), style: .default, handler: { _ in
|
||||
DLog("Max registrations error, request deletion tapped")
|
||||
|
||||
let deleteUrl = URLHelper.getDataDeletionURL()
|
||||
guard let url = URL(string: deleteUrl) else {
|
||||
DLog("Unable to create url")
|
||||
return
|
||||
}
|
||||
|
||||
let safariVC = SFSafariViewController(url: url)
|
||||
self?.present(safariVC, animated: true, completion: nil)
|
||||
|
||||
}))
|
||||
errorAlert.addAction(UIAlertAction(title: "max_registration_button2".localizedString(), style: .default, handler: { _ in
|
||||
DLog("Max registrations error, close alert")
|
||||
}))
|
||||
} else {
|
||||
errorAlert.addAction(UIAlertAction(title: "global_OK".localizedString(), style: .default, handler: { _ in
|
||||
DLog("Unable to verify phone number")
|
||||
}))
|
||||
}
|
||||
self?.present(errorAlert, animated: true)
|
||||
DLog("Phone number verification error: \(error.localizedDescription)")
|
||||
return
|
||||
|
|
|
@ -21,7 +21,7 @@ class QuestionUploadDataViewController: UIViewController {
|
|||
// MARK: -
|
||||
|
||||
private func showUploadDataFlow() {
|
||||
let keychain = KeychainSwift()
|
||||
let keychain = KeychainSwift.shared
|
||||
setIsLoading(true)
|
||||
guard let jwt = keychain.get("JWT_TOKEN") else {
|
||||
DLog("Error trying to upload when not logged in")
|
||||
|
|
|
@ -11,7 +11,7 @@ import KeychainSwift
|
|||
|
||||
class RegistrationIntroViewController: UIViewController {
|
||||
|
||||
let keychain = KeychainSwift()
|
||||
let keychain = KeychainSwift.shared
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
|
|
@ -64,7 +64,6 @@ class RegistrationSuccessViewController: UIViewController {
|
|||
if reauthenticating {
|
||||
dismiss(animated: true, completion: nil)
|
||||
} else {
|
||||
let homeVC = HomeViewController(nibName: "HomeView", bundle: nil)
|
||||
let tabVC = MainTabBarViewController()
|
||||
self.navigationController?.setViewControllers([tabVC], animated: true)
|
||||
}
|
||||
|
|
|
@ -62,4 +62,8 @@ struct URLHelper {
|
|||
return "\(getHelpURL())#location-permissions"
|
||||
}
|
||||
|
||||
static func getDataDeletionURL() -> String {
|
||||
return "https://covidsafe-form.service.gov.au"
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import KeychainSwift
|
|||
final class UploadHelper {
|
||||
|
||||
public static func uploadEncounterData(pin: String?, _ result: @escaping (UploadResult, String?) -> Void) {
|
||||
let keychain = KeychainSwift()
|
||||
let keychain = KeychainSwift.shared
|
||||
|
||||
guard let managedContext = EncounterDB.shared.persistentContainer?.viewContext else {
|
||||
result(.Failed, "[001]")
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"[test]decommission_message" = "";
|
||||
"action_continue" = "تابع";
|
||||
"action_report_an_issue" = "بلِّغ عن مشكلة";
|
||||
"action_upload_done" = "تمّ";
|
||||
|
@ -286,6 +287,8 @@
|
|||
"factors_intro_2" = "كما يمكن أن تتأثر المصافحات بالعوامل البيئية، بما في ذلك:";
|
||||
/* Example: "From 22 to 28 January 2021" */
|
||||
"from" = "";
|
||||
/* Error message for when a user tries to signup outside of Australia. */
|
||||
"geoblock_error_message" = "";
|
||||
"global_back" = "رجوع";
|
||||
"global_close" = "إغلاق";
|
||||
"global_double_tap" = "أنقر مرتين للتصحيح";
|
||||
|
@ -367,6 +370,9 @@
|
|||
"location_off" = "الموقع الجغرافي: معطّل";
|
||||
"location_off_description" = "يتطلب جهاز iphone الخاص بك تشغيل الموقع الجغرافي ليتمكن تطبيق COVIDSafe من العمل. لا يقوم تطبيق COVIDSafe بتتبّع بيانات الموقع الجغرافي أو تخزينها.";
|
||||
"main_restrictions" = "القيود الرئيسية";
|
||||
"max_registration_button2" = "";
|
||||
"max_registrations" = "";
|
||||
"max_registrations_button1" = "";
|
||||
/* Splash Screen */
|
||||
"migration_in_progress" = "تحديث تطبيق COVIDSafe قيد الإنجاز. \n\nيُرجى التأكد من عدم إغلاق هاتفك إلى أن يتم اكتمال التحديث.";
|
||||
"minute" = "دقيقة";
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"[test]decommission_message" = "";
|
||||
"action_continue" = "Συνεχίστε";
|
||||
"action_report_an_issue" = "Αναφέρετε το πρόβλημα";
|
||||
"action_upload_done" = "Συνεχίστε";
|
||||
|
@ -286,6 +287,8 @@
|
|||
"factors_intro_2" = "Οι χειραψίες μπορούν επίσης να επηρεαστούν από περιβαλλοντικούς παράγοντες, όπως:";
|
||||
/* Example: "From 22 to 28 January 2021" */
|
||||
"from" = "";
|
||||
/* Error message for when a user tries to signup outside of Australia. */
|
||||
"geoblock_error_message" = "";
|
||||
"global_back" = "Πίσω";
|
||||
"global_close" = "Κλείστε";
|
||||
"global_double_tap" = "πατήστε δύο φορές για επεξεργασία";
|
||||
|
@ -367,6 +370,9 @@
|
|||
"location_off" = "Τοποθεσία: ΑΠΕΝΕΡΓΟΠΟΙΗΜΕΝΗ";
|
||||
"location_off_description" = "Το iPhone σας απαιτεί άδεια τοποθεσίας για να λειτουργήσει την COVIDSafe. Η COVIDSafe ΔΕΝ παρακολουθεί ούτε αποθηκεύει τα δεδομένα τοποθεσίας σας.";
|
||||
"main_restrictions" = "Κύριοι περιορισμοί";
|
||||
"max_registration_button2" = "";
|
||||
"max_registrations" = "";
|
||||
"max_registrations_button1" = "";
|
||||
/* Splash Screen */
|
||||
"migration_in_progress" = "Η ενημέρωση της COVIDSafe συνεχίζεται. \n\nΒεβαιωθείτε ότι το τηλέφωνό σας δεν είναι απενεργοποιημένο έως ότου ολοκληρωθεί η ενημέρωση.";
|
||||
"minute" = "Λεπτό";
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"[test]decommission_message" = "Delete this app now";
|
||||
"action_continue" = "Continue";
|
||||
"action_report_an_issue" = "Report an issue";
|
||||
"action_upload_done" = "Done";
|
||||
|
@ -286,6 +287,8 @@
|
|||
"factors_intro_2" = "Handshakes can also be affected by environmental factors, including:";
|
||||
/* Example: "From 22 to 28 January 2021" */
|
||||
"from" = "From";
|
||||
/* Error message for when a user tries to signup outside of Australia. */
|
||||
"geoblock_error_message" = "You may be connected using an internet provider from outside Australia. This could happen when using international data roaming on a non-Australian SIM card.\n\nPlease connect to an Australian network or use a local Wi-Fi internet connection to continue.";
|
||||
"global_back" = "Back";
|
||||
"global_close" = "Close";
|
||||
"global_double_tap" = "double tap to edit";
|
||||
|
@ -367,6 +370,9 @@
|
|||
"location_off" = "Location: OFF";
|
||||
"location_off_description" = "Your iPhone requires Location permission for COVIDSafe to work. COVIDSafe does NOT track or store your location data.";
|
||||
"main_restrictions" = "Main restrictions";
|
||||
"max_registration_button2" = "Cancel";
|
||||
"max_registrations" = "You've reached the limit to register using the same mobile number.\n\nTo use the same mobile number to register again, you must request to delete your current registration information.";
|
||||
"max_registrations_button1" = "Request deletion";
|
||||
/* Splash Screen */
|
||||
"migration_in_progress" = " COVIDSafe update in progress. \n\n Please make sure you phone is not switched off until the update is complete.";
|
||||
"minute" = "Minute";
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"[test]decommission_message" = "";
|
||||
"action_continue" = "Continua";
|
||||
"action_report_an_issue" = "Segnala un problema";
|
||||
"action_upload_done" = "Eseguito";
|
||||
|
@ -286,6 +287,8 @@
|
|||
"factors_intro_2" = "Anche le strette di mano possono essere influenzate da fattori ambientali:";
|
||||
/* Example: "From 22 to 28 January 2021" */
|
||||
"from" = "";
|
||||
/* Error message for when a user tries to signup outside of Australia. */
|
||||
"geoblock_error_message" = "";
|
||||
"global_back" = "Indietro";
|
||||
"global_close" = "Chiudi";
|
||||
"global_double_tap" = "Fai un doppio click per modificare";
|
||||
|
@ -367,6 +370,9 @@
|
|||
"location_off" = "Posizione: OFF";
|
||||
"location_off_description" = "Il tuo iPhone richiede l'autorizzazione alla localizzazione affinché COVIDSafe funzioni. COVIDSafe NON memorizza né utilizza i dati relativi alla tua posizione.";
|
||||
"main_restrictions" = "Restrizioni principali";
|
||||
"max_registration_button2" = "";
|
||||
"max_registrations" = "";
|
||||
"max_registrations_button1" = "";
|
||||
/* Splash Screen */
|
||||
"migration_in_progress" = "Aggiornamento COVIDSafe in corso. \n\nAssicurati che il cellulare non sia spento fino al completamento dell'aggiornamento.";
|
||||
"minute" = "Minuto";
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"[test]decommission_message" = "";
|
||||
"action_continue" = "계속";
|
||||
"action_report_an_issue" = "문제 신고하기";
|
||||
"action_upload_done" = "완료";
|
||||
|
@ -286,6 +287,8 @@
|
|||
"factors_intro_2" = "다음과 같은 환경 요인 역시 블루투스 악수에 영향을 미칠 수 있습니다.";
|
||||
/* Example: "From 22 to 28 January 2021" */
|
||||
"from" = "부터";
|
||||
/* Error message for when a user tries to signup outside of Australia. */
|
||||
"geoblock_error_message" = "";
|
||||
"global_back" = "뒤로";
|
||||
"global_close" = "닫기";
|
||||
"global_double_tap" = "두 번 탭해서 수정하세요";
|
||||
|
@ -367,6 +370,9 @@
|
|||
"location_off" = "위치 서비스: 꺼짐";
|
||||
"location_off_description" = "COVIDSafe가 제대로 실행되기 위해서는 여러분의 아이폰에서 위치 서비스 기능을 허용해야 합니다. COVIDSafe는 여러분의 위치 정보를 추적하거나 저장하지 않습니다.";
|
||||
"main_restrictions" = "주요 규제 조치";
|
||||
"max_registration_button2" = "";
|
||||
"max_registrations" = "";
|
||||
"max_registrations_button1" = "";
|
||||
/* Splash Screen */
|
||||
"migration_in_progress" = "COVIDSafe 업데이트 진행 중. \n\n업데이트가 완료될 때까지 휴대폰이 꺼지지 않도록 해주세요.";
|
||||
"minute" = "분";
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"[test]decommission_message" = "";
|
||||
"action_continue" = "ਜਾਰੀ ਰੱਖੋ";
|
||||
"action_report_an_issue" = "ਮੁੱਦੇ/ਮਸਲੇ ਬਾਰੇ ਰਿਪੋਰਟ ਕਰੋ|";
|
||||
"action_upload_done" = "ਕਰ ਦਿੱਤਾ";
|
||||
|
@ -286,6 +287,8 @@
|
|||
"factors_intro_2" = "ਹੱਥ ਮਿਲਾਉਣੇ, ਵਾਤਾਵਰਣਕ ਕਾਰਕਾਂ ਦੁਆਰਾ ਵੀ ਪ੍ਰਭਾਵਤ ਹੋ ਸਕਦੇ ਹਨ, ਜਿੰਨ੍ਹਾਂ ਵਿੱਚ ਸ਼ਾਮਲ ਹਨ :";
|
||||
/* Example: "From 22 to 28 January 2021" */
|
||||
"from" = "ਵੱਲੋਂ";
|
||||
/* Error message for when a user tries to signup outside of Australia. */
|
||||
"geoblock_error_message" = "";
|
||||
"global_back" = "ਪਿੱਛੇ";
|
||||
"global_close" = "ਬੰਦ ਕਰੋ";
|
||||
"global_double_tap" = "ਸੋਧਣ ਲਈ ਡਬਲ ਟੈਪ ਕਰੋ";
|
||||
|
@ -367,6 +370,9 @@
|
|||
"location_off" = "ਲੋਕੇਸ਼ਨ: ਔਫ";
|
||||
"location_off_description" = "COVIDSafe ਕੰਮ ਕਰ ਸਕੇ, ਇਸ ਦੇ ਲਈ ਤੁਹਾਡੇ iPhone ਨੂੰ ਲੋਕੇਸ਼ਨ ਆਗਿਆ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ। *COVIDSafe ਤੁਹਾਡੇ ਸਥਾਨਕ ਅੰਕੜਿਆਂ ਨੂੰ ਨਹੀਂ ਢੂੰਡਦਾ ਜਾਂ ਸੰਭਾਲਦਾ।*";
|
||||
"main_restrictions" = "ਮੁੱਖ ਪਾਬੰਦੀਆਂ";
|
||||
"max_registration_button2" = "";
|
||||
"max_registrations" = "";
|
||||
"max_registrations_button1" = "";
|
||||
/* Splash Screen */
|
||||
"migration_in_progress" = "COVIDSafe ਅੱਪਡੇਟ ਚੱਲ ਰਿਹਾ ਹੈ। \n\nਕਿਰਪਾ ਕਰਕੇ ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਤੁਹਾਡਾ ਫ਼ੋਨ ਤਦ ਤੱਕ ਬੰਦ ਨਹੀਂ ਹੋਣਾ ਚਾਹੀਦਾ ਜਦ ਤੱਕ ਅੱਪਡੇਟ ਪੂਰਾ ਨਹੀਂ ਹੋ ਜਾਂਦਾ।";
|
||||
"minute" = "ਮਿੰਟ";
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"[test]decommission_message" = "";
|
||||
"action_continue" = "Devam";
|
||||
"action_report_an_issue" = "Sorun bildir";
|
||||
"action_upload_done" = "Bitti";
|
||||
|
@ -286,6 +287,8 @@
|
|||
"factors_intro_2" = "El sıkışmalar çevresel faktörlerden de etkilenebilir, örneğin:";
|
||||
/* Example: "From 22 to 28 January 2021" */
|
||||
"from" = "";
|
||||
/* Error message for when a user tries to signup outside of Australia. */
|
||||
"geoblock_error_message" = "";
|
||||
"global_back" = "Geri";
|
||||
"global_close" = "Kapat";
|
||||
"global_double_tap" = "düzeltme yapmak için iki defa tıklayınız";
|
||||
|
@ -367,6 +370,9 @@
|
|||
"location_off" = "Konum: KAPALI";
|
||||
"location_off_description" = "iPhone’unuz, COVIDSafe’in çalışması için Konum iznine ihtiyaç duyar. COVIDSafe konum verilerinizi İZLEMEZ veya SAKLAMAZ.";
|
||||
"main_restrictions" = "Ana kısıtlamalar";
|
||||
"max_registration_button2" = "";
|
||||
"max_registrations" = "";
|
||||
"max_registrations_button1" = "";
|
||||
/* Splash Screen */
|
||||
"migration_in_progress" = " COVIDSafe güncellemesi devam ediyor. \n\n Lütfen güncelleme tamamlanana kadar telefonunuzu kapatmayın.";
|
||||
"minute" = "Dakika";
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"[test]decommission_message" = "";
|
||||
"action_continue" = "Tiếp tục";
|
||||
"action_report_an_issue" = "Báo cáo sự cố";
|
||||
"action_upload_done" = "Hoàn tất";
|
||||
|
@ -286,6 +287,8 @@
|
|||
"factors_intro_2" = "Chức năng bắt tay cũng có thể bị ảnh hưởng bởi các yếu tố môi trường, bao gồm:";
|
||||
/* Example: "From 22 to 28 January 2021" */
|
||||
"from" = "";
|
||||
/* Error message for when a user tries to signup outside of Australia. */
|
||||
"geoblock_error_message" = "";
|
||||
"global_back" = "Trở lại";
|
||||
"global_close" = "Đóng";
|
||||
"global_double_tap" = "nhấn đúp để chỉnh sửa";
|
||||
|
@ -367,6 +370,9 @@
|
|||
"location_off" = "Vị trí: TẮT";
|
||||
"location_off_description" = "iPhone của bạn yêu cầu quyền truy cập Vị trí để COVIDSafe hoạt động. COVIDSafe KHÔNG theo dõi hoặc lưu trữ dữ liệu vị trí của bạn.";
|
||||
"main_restrictions" = "Những hạn chế chính";
|
||||
"max_registration_button2" = "";
|
||||
"max_registrations" = "";
|
||||
"max_registrations_button1" = "";
|
||||
/* Splash Screen */
|
||||
"migration_in_progress" = "COVIDSafe trong tiến trình cập nhật. \n\nVui lòng đảm bảo điện thoại của bạn không bị tắt cho đến khi cập nhật hoàn tất.";
|
||||
"minute" = "Phút";
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"[test]decommission_message" = "";
|
||||
"action_continue" = "继续";
|
||||
"action_report_an_issue" = "报告问题";
|
||||
"action_upload_done" = "完成";
|
||||
|
@ -286,6 +287,8 @@
|
|||
"factors_intro_2" = "蓝牙握手也会受到环境因素的影响,这些因素包括:";
|
||||
/* Example: "From 22 to 28 January 2021" */
|
||||
"from" = "";
|
||||
/* Error message for when a user tries to signup outside of Australia. */
|
||||
"geoblock_error_message" = "";
|
||||
"global_back" = "返回";
|
||||
"global_close" = "关闭";
|
||||
"global_double_tap" = "双击进行编辑";
|
||||
|
@ -367,6 +370,9 @@
|
|||
"location_off" = "位置:关闭";
|
||||
"location_off_description" = "您的 iPhone 要求COVIDSafe 获得位置权限才能运行。COVIDSafe 不会跟踪或存储您的位置数据。";
|
||||
"main_restrictions" = "主要限制措施";
|
||||
"max_registration_button2" = "";
|
||||
"max_registrations" = "";
|
||||
"max_registrations_button1" = "";
|
||||
/* Splash Screen */
|
||||
"migration_in_progress" = "COVIDSafe正在更新。 \n\n请保持开机状态直至更新完成。";
|
||||
"minute" = "分钟";
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"[test]decommission_message" = "";
|
||||
"action_continue" = "繼續";
|
||||
"action_report_an_issue" = "報告問題";
|
||||
"action_upload_done" = "完成";
|
||||
|
@ -286,6 +287,8 @@
|
|||
"factors_intro_2" = "藍牙握手也可以受到環境因素影響,包括:";
|
||||
/* Example: "From 22 to 28 January 2021" */
|
||||
"from" = "";
|
||||
/* Error message for when a user tries to signup outside of Australia. */
|
||||
"geoblock_error_message" = "";
|
||||
"global_back" = "返回";
|
||||
"global_close" = "關閉";
|
||||
"global_double_tap" = "點觸兩次來編輯";
|
||||
|
@ -367,6 +370,9 @@
|
|||
"location_off" = "位置:關閉";
|
||||
"location_off_description" = "你的iPhone要求COVIDSafe獲得位置許可權才能正常運作。COVIDSafe不會跟蹤或者存儲你的位置資料。";
|
||||
"main_restrictions" = "主要限制措施";
|
||||
"max_registration_button2" = "";
|
||||
"max_registrations" = "";
|
||||
"max_registrations_button1" = "";
|
||||
/* Splash Screen */
|
||||
"migration_in_progress" = "正在更新 COVIDSafe。\n\n請保持開機狀態直至更新完成為止。";
|
||||
"minute" = "分鐘 ";
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue