Informacje
Treść

[Android] 2. Dokumentacja API

 CMPManager Klasa udostępnia metody zarządzania zgodą użytkownika na przetwarzanie i śledzenie danych. Ta dokumentacja obejmuje główne metody dostępne dla integracji aplikacji mobilnych.

Wszystkie podane poniżej przykłady zostały zaczerpnięte z naszej witryny i można je znaleźć na niej. Aplikacja demonstracyjna.

Proszę sprawdzić przestarzałe metody w przypadku, gdy integrujesz już wersję 3 naszego pakietu CMP SDK.

Inicjalizacji

Konfiguracja Url()

Ustawia konfigurację adresu URL dla Consent Manager. Należy zainicjować wartością poniżej i przekazać do getInstance Metoda. 

Parametry:

  • id:String — kod identyfikacyjny pobrany z pulpitu CMP
  • domain:String — Domena do zarządzania zgodą, pobierana również z pulpitu CMP
  • language:String - Kod języka (np. „EN”, „IT”, „DE” itd.)
  • appName:String — nazwa Twojej aplikacji, używana wyłącznie do celów raportowania
  • jsonConfig:Obiekt JSON — opcjonalny, używany tylko w szczególnych przypadkach, nie należy go używać, chyba że jest wyraźnie podany na pulpicie CMP wraz z pozostałymi parametrami powyżej. 

Przykład:

val urlConfig = UrlConfig(
    id = "YOUR_CODE_ID_HERE",
    domain = "delivery.consentmanager.net",
    language = "EN",
    appName = "CMDemoAppKotlin"
)

ustawAktywność()

Ustawia Activity, która będzie prezentować warstwę zgody. Powinna to być ComponentActivity

Parametry:

  • viewController:ComponentActivity — Aktywność, w której zostanie zaprezentowana warstwa zgody.

Zwraca: żaden

Przykład:

CMPManager.shared.setPresentingViewController(self)

 

Parametry ConsentLayerUIConfig()

  • position:Strategia pozycjonowania dialogu (domyślna: Position.FULL_SCREEN):
    • Position.FULL_SCREEN - Pokrywa cały ekran
    • Position.HALF_SCREEN_TOP - Górna połowa ekranu
    • Position.HALF_SCREEN_BOTTOM - Dolna połowa ekranu
    • Position.custom(width: Int, height: Int) - Niestandardowe wymiary w pikselach
  • backgroundStyle: Wygląd tła (domyślnie: `BackgroundStyle.dimmed(Color.BLACK, 0.5f))
  • cornerRadius: Float - Promień narożnika w dp (domyślnie: 0f).
    • Note: Wymagana jest wersja SDK 3.7.0 lub nowsza do prawidłowego przycinania zawartości WebView
  • respectsSafeArea: Boolean - Czy szanować wstawki interfejsu użytkownika systemu (domyślnie: true).
    • Note:W systemie Android 11+ (API 30) ustawienie false pozwala na rozszerzenie okna dialogowego pod paskami systemowymi
  • isCancelable: Boolean - Czy okno dialogowe jest możliwe do zamknięcia (domyślnie: true)
  • darkMode: Boolean - Włącz stylizację trybu ciemnego (domyślnie: false)
//Example - Custom Position

val displayMetrics = resources.displayMetrics
val customPosition = ConsentLayerUIConfig.Position.custom(
    displayMetrics.widthPixels,
    (displayMetrics.heightPixels * 0.8).toInt() // 80% screen height
)

val webViewConfig = ConsentLayerUIConfig(
	position = customPosition,
	cornerRadius = 20f,
	respectsSafeArea = true
)

pobierzStatusUżytkownika()

Zwraca szczegółową migawkę bieżącego statusu zgody i preferencji użytkownika. Ta metoda dostarcza kompleksowych informacji o wyborach zgody użytkownika, w tym ich ogólnego statusu zgody, indywidualnych uprawnień dostawcy, zgód specyficznych dla celu i odpowiednich ciągów zgody.

Parametry:

  • żaden

Zwraca:

Typ zwrotu: CMPUserStatusResponse obiekt, wyjaśniony w kodzie poniżej. 

Przykład:

let status = CMPManager.shared.getUserStatus()
var message = "Status: \(status.status)\n\n"

message += "Vendors:\n"
for (vendorId, state) in status.vendors {
    message += "- \(vendorId): \(state)\n"
}

message += "\nPurposes:\n"
for (purposeId, state) in status.purposes {
    message += "- \(purposeId): \(state)\n"
}

message += "\nTCF: \(status.tcf)\n"
message += "Additional Consent: \(status.addtlConsent)\n"
message += "Regulation: \(status.regulation)"

print(message)

sprawdźIotwórz()

Sprawdza na serwerze, czy wymagana jest zgoda i otwiera warstwę zgody, jeśli jest to konieczne. Spowoduje to wykonanie wywołania sieciowego do naszych serwerów za pośrednictwem WKWebView utworzonego w naszym SDK, zużywanie jednej odsłony w trakcie. To wywołanie sieciowe wyśle ​​wiadomość do naszego zaplecza za pośrednictwem JavaScript, który wykryje, czy urządzenie ma ważną zgodę, czy nie, co z kolei określi, czy warstwa zgody musi zostać wyświetlona, ​​czy nie.  

Parametry:

  • jumpToSettings:Wartość logiczna określająca, czy wyświetlana warstwa zgody automatycznie przekieruje do strony, na której użytkownicy będą mogli uzyskać bardziej szczegółową kontrolę nad zgodami udzielonymi przez użytkowników, co umożliwi im dokładne dostrojenie swoich wyborów (gdy ustawiono na true) lub początkowy ekran domyślny z przyciskami (gdy ustawiony na false lub stłumione).
  • completion: Zamknięcie wywoływane po zakończeniu operacji, którego wynikiem jest: true or false.

Zwraca: żaden

Przykład:

cmpManager.checkAndOpen { result ->
    result.onSuccess {
	    toastMessage = "Check and Open Consent Layer operation done successfully."
    }.onFailure { error ->
    	toastMessage = "Check and Open Consent Layer operation failed with error: $error"
}

wymuśOtwarcie()

Parametry:

  • jumpToSettings:Wartość logiczna określająca, czy wyświetlana warstwa zgody automatycznie przekieruje do strony, na której użytkownicy będą mogli uzyskać bardziej szczegółową kontrolę nad zgodami udzielonymi przez użytkowników, co umożliwi im dokładne dostrojenie swoich wyborów (gdy ustawiono na true) lub początkowy ekran domyślny z przyciskami (gdy ustawiony na false lub stłumione).
  • completion: Zamknięcie wywoływane po zakończeniu operacji, zwracając albo pomyślnie, albo błędnie. 

Zwraca: żaden

Przykład:

cmpManager.openConsentLayer { result ->
	result.onFailure { error ->
    	toastMessage = "Error: ${error.message}"
	}
}

eksportujCMPInfo()

Eksportuje bieżące informacje o zgodzie zapisane na urządzeniu jako ciąg. Ta metoda pobiera ciąg zgody z obszaru SharedPreferences urządzenia i zwraca go. Zwykle te informacje są przekazywane do importCMPInfo Metoda.

Zwraca: Ciąg - eksportowane informacje o zgodzie

Przykład:

val cmpInfo = CMPManager.shared.exportCMPInfo()
Log.d("Exported CMP info: \(cmpInfo)")

pobierzStatusTrybuZgodyGoogle()

Bezproblemowo integruje się z trybem zgody, technologią Google umożliwiającą modelowanie konwersji i analiz, umożliwiając usługom Google uzupełnianie luk w danych, gdy użytkownicy nie wyrażają na to zgody. Ta funkcja tłumaczy zgodę użytkownika udzieloną przez CMP na format zrozumiały dla Firebase Analytics, dzięki czemu możesz po prostu pobrać wynik tej metody i przekazać go do metody Firebase .setConsent.

  • Następnie aktualizuje Google Analytics o aktualny stan zgody użytkownika.

Parametry:

  • żaden

Zwraca: Map<String, String> - Tablica wartości kluczowych zawierająca cztery klucze trybu zgody Google: .analyticsStorage, .adStorage, .adUserData oraz .adPersonalizationi ich odpowiednie wartości w odniesieniu do .choiceDoesntExist, .granted or .denied.

Przykład:

val settings = cmpManager.getGoogleConsentModeStatus()
Log.d("CMPDemo", "Google Consent Mode Settings: $settings")
toastMessage = buildString {
  append("Google Consent Settings:")
  settings.forEach { (key, value) ->
	  append("\n$key: $value")
  }
}

pobierzStatusForPurpose()

Parametry:

  • id:String - Identyfikator celu sprawdzenia

Zwraca: UniqueConsentStatus - Wyliczenie z wartościami .choiceDoesntExist jeśli nie wyrażono zgody, .granted or .denied.

Przykład:

val purposeStatus = cmpManager.getStatusForPurpose("c53")
var message = "Vendor s2789's status: "
switch purposeStatus {
   	case .choiceDoesntExist: message += "No Choice"
    case .granted: message += "Granted"
    case .denied: message += "Denied"
    @unknown default: message += "No Choice"
}

pobierzStatusDlaDostawcy()

Parametry:

  • id:String - Identyfikator celu sprawdzenia

Zwraca: UniqueConsentStatus - Wyliczenie z wartościami .choiceDoesntExist jeśli nie wyrażono zgody, .granted or .denied.

Przykład:

val vendorStatus = cmpManager.getStatusForVendor("s2789")
var message = "Vendor s2789's status: "
switch vendorStatus {
   	case .choiceDoesntExist: message += "No Choice"
    case .granted: message += "Granted"
    case .denied: message += "Denied"
    @unknown default: message += "No Choice"
}

 

zaakceptujWszystko()

Parametry:

  • completion: Zamknięcie wywoływane po zakończeniu operacji, zwracając komunikat o niepowodzeniu lub powodzeniu.

Zwraca: żaden

Przykład:

cmpManager.acceptAll { result ->
    result.onSuccess {
	    toastMessage = "All consents accepted"
  	}.onFailure { error ->
		toastMessage = "Error: ${error.message}"
	}
}

zaakceptujCele()

Parametry:

  • purposes:Lista - Lista identyfikatorów celów do zaakceptowania
  • updatePurpose: Boolean - Czy aktualizować powiązane cele
  • completion: Zamknięcie wywoływane po zakończeniu operacji, zwracając albo błąd, albo powodzenie

Zwraca: żaden

Przykład:

cmpManager.acceptPurposes(listOf("c52", "c53"), true) { result ->
	  	result.onSuccess {
	  	toastMessage = "Purposes enabled"
  	}.onFailure { error ->
  		toastMessage = "Error: ${error.message}"
    }
}

zaakceptujDostawców()

Parametry:

  • vendors:Lista - Lista identyfikatorów dostawców do zaakceptowania
  • completion: Zamknięcie wywoływane po zakończeniu operacji

Zwraca: żaden

Przykład:

cmpManager.acceptVendors(listOf("s2790", "s2791")) { result ->
	result.onSuccess {
		toastMessage = "Vendors Enabled"
	}.onFailure { error ->
		toastMessage = "Error: ${error.message}"
	}
}

importCMPInfo()

Parametry:

  • cmpString:String - Ciąg CMP do zaimportowania
  • completion: Zamknięcie wywoływane po zakończeniu operacji, zwracając błąd lub powodzenie

Zwraca: żaden

Przykład:

val cmpString = "Q1FERkg3QVFERkg3QUFmR01CSVRCQkVnQUFBQUFBQUFBQWlnQUFBQUFBQUEjXzUxXzUyXzUzXzU0XzU1XzU2XyNfczI3ODlfczI3OTBfczI3OTFfczI2OTdfczk3MV9VXyMxLS0tIw"
cmpManager.importCMPInfo(cmpString) { result ->
	result.onSuccess {
		toastMessage = "Vendors Enabled"
	}.onFailure { error ->
		toastMessage = "Error: ${error.message}"
	}
}

odrzućWszystko()

Parametry:

  • completion: Zamknięcie wywoływane po zakończeniu operacji

Zwraca: żaden

Przykład:

cmpManager.rejectAll { result ->
	result.onSuccess {
		toastMessage = "All consents rejected"
	}.onFailure { error ->
		toastMessage = "Error: ${error.message}"
	}
}

odrzućCele()

Parametry:

  • purposes: List<String> - Lista identyfikatorów celów do odrzucenia
  • updateVendor: Boolean - Czy aktualizować powiązanych dostawców
  • completion: Zamknięcie wywoływane po zakończeniu operacji

Zwraca: żaden

Przykład:

cmpManager.rejectPurposes(listOf("c52", "c53"), true) { result ->
    result.onSuccess {
      toastMessage = "Purposes disabled"
    }.onFailure { error ->
      toastMessage = "Error: ${error.message}"
	}
}

odrzućDostawców()

Parametry:

  • vendors:Lista - Lista identyfikatorów dostawców do odrzucenia
  • completion: Zamknięcie wywoływane po zakończeniu operacji, zwracając albo błąd, albo powodzenie

Zwraca: żaden

Przykład:

cmpManager.rejectVendors(listOf("s2790", "s2791")) { result ->
	result.onSuccess {
		toastMessage = "Vendors Disabled"
	}.onFailure { error ->
		toastMessage = "Error: ${error.message}"
	}
}

resetConsentManagementData()

Parametry:

żaden

Zwraca: żaden

Przykład:

cmpManager.resetConsentManagementData()

Przekazywanie danych o zgodzie do zestawów SDK stron trzecich

aktualizacjaZgody stron trzecich

Ta metoda obsługuje automatyczne przekazywanie danych zgody do zestawów SDK stron trzecich, takich jak AppsFlyer, AdJust, Branch, Kochava, Singular, AirBridge i Tenjin. Działa poprzez introspekcję/refleksję, więc zainicjuj zestaw SDK strony trzeciej swoimi danymi uwierzytelniającymi, korzystając ze zwykłej strategii zalecanej przez dostawcę, a nasz zestaw SDK CMP wykryje wystąpienie zestawu SDK 3P i wywoła niezbędne metody w tle. 

 

CMPManagerDelegat wydarzenia

ustawOnClickLinkCallback

Zestaw SDK zapewnia elastyczny mechanizm obsługi łączy, który umożliwia aplikacjom dostosowywanie sposobu obsługi adresów URL w warstwie zgody. Domyślnie wszystkie łącza otwierają się w WebView, ale aplikacje mogą przechwytywać określone adresy URL, aby obsługiwać je zewnętrznie, gdy jest to potrzebne.

        cmpManager.setOnClickLinkCallback { url ->
            if (url.contains("google.com")) {
                // Open Google URLs in external browser
                try {
                    startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
                    true // Return true to indicate we handled the URL
                } catch (e: Exception) {
                    Log.e("DemoApp", "Error opening URL: $url", e)
                    false
                }
            } else {
                // Let other URLs load in the WebView
                false
            }
        }

didReceiveConsent(zgoda: String, jsonObject: Mapa )

Wyzwala się, gdy warstwa zgody została zamknięta po zaktualizowaniu przez użytkownika swoich zgód LUB podczas wywołania metod powodujących zmiany w zgodach, takich jak acceptAll, rejectAll, acceptVendors, rejectVendors itd. Oznacza to, że użytkownik zaakceptował lub odrzucił część lub wszystkie zgody, a także że zostały one prawidłowo zapisane na urządzeniu.

Wyświetl warstwę zgody

Wyzwala się, gdy warstwa zgody została faktycznie wyświetlona. Oznacza to, że w urządzeniu nie było ważnej zgody, więc należy zebrać nową.

zamknij warstwę zgody


Wyzwalane jest, gdy SDK sprawdza potrzebę zgody, ale nie była ona potrzebna i warstwa nie była wyświetlana. Oznacza to, że w urządzeniu jest już prawidłowa, więc nowa nie jest konieczna i warstwa zgody nie zostanie wyświetlona.

otrzymałem błąd

Funkcja ta jest aktywowana, gdy zestaw SDK napotka błąd i zwróci swój kod.

Przestarzałe metody

Wszystkie poniższe metody zostały wycofane i zostanie całkowicie usunięty z SDK z Lipiec/2025 jeden. 

checkIfConsentIsRequired()

Parametry:

  • completion: (Boolean) -> Jednostka - Zamknięcie wywoływane z wynikiem, albo true or false.

Zwraca: żaden

Przykład:

cmpManager.checkIfConsentIsRequired { needsConsent ->
	toastMessage = "Needs Consent: $needsConsent"
}

checkWithServerAndOpenIfNecessary()

Sprawdza na serwerze, czy wymagana jest zgoda i otwiera warstwę zgody, jeśli jest to konieczne. Spowoduje to wywołanie sieciowe do naszych serwerów za pośrednictwem WebView utworzonego w naszym SDK, zużywanie jednej odsłony w trakcie. To wywołanie sieciowe wyśle ​​wiadomość do naszego zaplecza za pośrednictwem JavaScript, który wykryje, czy urządzenie ma ważną zgodę, czy nie, co z kolei określi, czy warstwa zgody musi zostać wyświetlona, ​​czy nie.  

Parametry:

  • completion: Zamknięcie wywoływane po zakończeniu operacji, którego wynikiem jest: true or false.

Zwraca: żaden

Przykład:

cmpManager.checkWithServerAndOpenIfNecessary { result ->
    result.onSuccess {
	    toastMessage = "Check and Open Consent Layer operation done successfully."
    }.onFailure { error ->
    	toastMessage = "Check and Open Consent Layer operation failed with error: $error"
}

pobierzWszystkieIdentyfikatory()

Pobiera wszystkie identyfikatory celów zapisane na urządzeniu, zgodnie z konfiguracjami CMP. Informacje te zostaną zaktualizowane dopiero po prawidłowym zapisaniu zgody w obszarze SharedPreferenceswięc jeśli sprawdzasz zaraz po użyciu metod, które wyzwalają zmiany w zgodzie, takich jak openConsentLayer, acceptAll or rejectAllna przykład, wtedy poczekaj, aż wywołanie zwrotne z tych metod zostanie wywołane przed uzyskaniem dostępu do metody hasUserChoice, aby upewnić się, że informacje są aktualne.

Zwraca: Lista - Lista identyfikatorów uniwersalnych

Przykład:

val allPurposes = cmpManager.getAllPurposesIDs()
Log.d("All purposes: \(allPurposes)")

pobierzIDWszystkichDostawców()

Pobiera wszystkie identyfikatory dostawców zapisane na urządzeniu, zgodnie z konfiguracjami CMP. Informacje te zostaną zaktualizowane dopiero po prawidłowym zapisaniu zgody w SharedPreferences obszarwięc jeśli sprawdzasz zaraz po użyciu metod, które wyzwalają zmiany w zgodzie, takich jak openConsentLayer, acceptAll or rejectAllna przykład, wtedy poczekaj, aż wywołanie zwrotne z tych metod zostanie wywołane przed uzyskaniem dostępu do metody hasUserChoice, aby upewnić się, że informacje są aktualne.

Zwraca: Lista - Lista wszystkich identyfikatorów dostawców

Przykład:

val allVendors = cmpManager.getAllVendorsIDs()
Log.d("All vendors: \(allVendors)")

pobierzWyłączoneIdentyfikatoryCelów()

Pobiera identyfikatory wszystkich wyłączonych celów zapisanych na urządzeniu, zgodnie z konfiguracjami CMP i wyborami użytkownika. Jeśli użytkownik zaakceptuje wszystkie zgody, będzie to puste.  Informacje te zostaną zaktualizowane dopiero po prawidłowym zapisaniu zgody w obszarze SharedPreferenceswięc jeśli sprawdzasz zaraz po użyciu metod, które wyzwalają zmiany w zgodzie, takich jak openConsentLayer, acceptAll or rejectAllna przykład, wtedy poczekaj, aż wywołanie zwrotne z tych metod zostanie wywołane przed uzyskaniem dostępu do metody hasUserChoice, aby upewnić się, że informacje są aktualne.

Zwraca: Lista - Lista wyłączonych identyfikatorów celów

Przykład:

val disabledPurposes = cmpManager.getDisabledPurposesIDs()
Log.d("Disabled purposes: \(disabledPurposes)")

pobierzWyłączoneIdentyfikatoryDostawców()

Pobiera identyfikatory wszystkich wyłączonych dostawców zapisanych na urządzeniu, zgodnie z konfiguracjami CMP. Jeśli użytkownik zaakceptuje wszystkie zgody, będzie to puste. Informacje te zostaną zaktualizowane dopiero po prawidłowym zapisaniu zgody w obszarze SharedPreferenceswięc jeśli sprawdzasz zaraz po użyciu metod, które wyzwalają zmiany w zgodzie, takich jak openConsentLayer, acceptAll or rejectAllna przykład, wtedy poczekaj, aż wywołanie zwrotne z tych metod zostanie wywołane przed uzyskaniem dostępu do metody hasUserChoice, aby upewnić się, że informacje są aktualne.

Zwraca: Lista - Lista wyłączonych identyfikatorów dostawców

Przykład:

val disabledVendors = CMPManager.shared.getDisabledVendorsIDs()
Log.d("Disabled vendors: \(disabledVendors)")

pobierzEnabledPurposesIDs()

Pobiera identyfikatory wszystkich włączonych celów zapisanych na urządzeniu, zgodnie z konfiguracjami CMP. Jeśli użytkownik odrzuci wszystkie zgody, będzie to puste. Informacje te zostaną zaktualizowane dopiero po prawidłowym zapisaniu zgody w obszarze SharedPreferenceswięc jeśli sprawdzasz zaraz po użyciu metod, które wyzwalają zmiany w zgodzie, takich jak openConsentLayer, acceptAll or rejectAllna przykład, wtedy poczekaj, aż wywołanie zwrotne z tych metod zostanie wywołane przed uzyskaniem dostępu do metody hasUserChoice, aby upewnić się, że informacje są aktualne.

Zwraca: Lista - Lista włączonych identyfikatorów celów

Przykład:

val enabledPurposes = cmpManager.getEnabledPurposesIDs()
Log.d("Enabled purposes: \(enabledPurposes)")

pobierzEnabledVendorsIDs()

Pobiera identyfikatory wszystkich włączonych dostawców zapisanych na urządzeniu. Jeśli użytkownik odrzuci wszystkie zgody, będzie to puste. Informacje te zostaną zaktualizowane dopiero po prawidłowym zapisaniu zgody w obszarze SharedPreferenceswięc jeśli sprawdzasz zaraz po użyciu metod, które wyzwalają zmiany w zgodzie, takich jak openConsentLayer, acceptAll or rejectAllna przykład, wtedy poczekaj, aż wywołanie zwrotne z tych metod zostanie wywołane przed uzyskaniem dostępu do metody hasUserChoice, aby upewnić się, że informacje są aktualne.

Zwraca: Lista - Lista włączonych identyfikatorów dostawców

Przykład:

val enabledVendors = cmpManager.getEnabledVendorsIDs()
Log.d("Enabled vendors: \(enabledVendors)")

ma zgodę na cel()

Parametry:

  • id:String - Identyfikator celu sprawdzenia

Zwraca: Boolean - True jeśli zgoda zostanie wyrażona, false Inaczej

Przykład:

val hasPurposeConsent = cmpManager.hasPurposeConsent(id: "c53")
Log.d("Has consent for purpose c53: \(hasPurposeConsent)")

maWybórUżytkownika()

Sprawdza, czy użytkownik dokonał wyboru dotyczącego zgód i czy ta zgoda jest przechowywana na urządzeniu. Oznacza to, że użytkownik albo zaakceptował wszystkie zgody, odrzucił wszystkie z nich, albo dokonał mieszanego wyboru odrzuconych i zaakceptowanych zgód, w zależności od projektu CMP, co może pozwolić użytkownikom na zaakceptowanie niektórych zgód i odrzucenie innych. Informacje te zostaną zaktualizowane dopiero po prawidłowym zapisaniu zgody w obszarze SharedPreferenceswięc jeśli sprawdzasz zaraz po użyciu metod, które wyzwalają zmiany w zgodzie, takich jak openConsentLayer, acceptAll or rejectAllna przykład, wtedy poczekaj, aż wywołanie zwrotne z tych metod zostanie wywołane przed uzyskaniem dostępu do metody hasUserChoice, aby upewnić się, że informacje są aktualne.

Zwraca: Boolean - true jeśli użytkownik dokonał wyboru, false Inaczej

Przykład:

val hasChoice = cmpManager.hasUserChoice()
print("User has made a choice: \(hasChoice)")

ma zgodę dostawcy()

Parametry:

  • id:String - Identyfikator dostawcy do sprawdzenia

Zwraca: Boolean - True jeśli zgoda zostanie wyrażona, false Inaczej

Przykład:

val hasVendorConsent = cmpManager.hasVendorConsent(id: "s2789")
Log.d("Has consent for vendor s2789: \(hasVendorConsent)")

 

 

Powrót do góry