// // Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // import Contacts import SignalServiceKit import SignalUI #if USE_DEBUG_UI class DebugContactsUtils: Dependencies { // MARK: Random Contacts static func randomPhoneNumber() -> String { var result: String var numberOfDigits: UInt if Bool.random() { // Generate a US phone number. result = "+1" numberOfDigits = 10 } else { // Generate a UK phone number. result = "+441" numberOfDigits = 9 } for _ in 0.. Void)? = nil ) async { guard count > 0 else { return } let authorizationStatus = CNContactStore.authorizationStatus(for: .contacts) guard authorizationStatus != .denied && authorizationStatus != .restricted else { OWSActionSheets.showErrorAlert(message: "No Contacts access.") return } // Request access guard authorizationStatus == .authorized else { let store = CNContactStore() let granted: Bool do { granted = try await store.requestAccess(for: .contacts) } catch { granted = false } guard granted else { DispatchQueue.main.async { OWSActionSheets.showErrorAlert(message: "No Contacts access.") } return } await createRandomContacts(count, contactHandler: contactHandler) return } let maxBatchSize: UInt = 10 let batchSize = min(maxBatchSize, count) let remainder = count - batchSize await createRandomContactsBatch(batchSize, contactHandler: contactHandler) guard remainder > 0 else { return } Task { @MainActor in await createRandomContacts(remainder, contactHandler: contactHandler) } } private static func createRandomContactsBatch( _ count: UInt, contactHandler: ((CNContact, Int, inout Bool) async -> Void)? ) async { Logger.debug("createRandomContactsBatch: \(count)") var contacts = [CNContact]() // 50% chance of fake contact having an avatar let percentWithAvatar = 50 let percentWithLargeAvatar = 25 let minimumAvatarDiameter: UInt = 200 let maximimAvatarDiameter: UInt = 800 let saveRequest = CNSaveRequest() for _ in 0.. Bool) { let keysToFetch: [CNKeyDescriptor] = [ CNContactIdentifierKey as CNKeyDescriptor, CNContactGivenNameKey as CNKeyDescriptor, CNContactFamilyNameKey as CNKeyDescriptor, CNContactFormatter.descriptorForRequiredKeys(for: .fullName) as CNKeyDescriptor ] let contactStore = CNContactStore() let saveRequest = CNSaveRequest() do { try contactStore.enumerateContacts(with: CNContactFetchRequest(keysToFetch: keysToFetch)) { contact, _ in if filter(contact) { saveRequest.delete(contact.mutableCopy() as! CNMutableContact) } } try contactStore.execute(saveRequest) } catch { Logger.error("\(error)") OWSActionSheets.showErrorAlert(message: error.userErrorDescription) } } } #endif