Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.margelo.nitro.playagerangedeclaration
import android.content.Context
import android.util.Log
import com.facebook.proguard.annotations.DoNotStrip
import com.google.android.play.agesignals.AgeSignalsManager
import com.google.android.play.agesignals.AgeSignalsManagerFactory
import com.google.android.play.agesignals.AgeSignalsRequest
import com.google.android.play.agesignals.AgeSignalsResult
Expand Down Expand Up @@ -30,32 +31,8 @@ class PlayAgeRangeDeclaration : HybridPlayAgeRangeDeclarationSpec() {
override fun getPlayAgeRangeDeclaration(): Promise<PlayAgeRangeDeclarationResult> {
return Promise.async {
try {
// MOCK: Using FakeAgeSignalsManager for testing (Commented out for production)
// https://developer.android.com/google/play/age-signals/test-age-signals-api
//
// To test different scenarios, change the AgeSignalsVerificationStatus:
// - AgeSignalsVerificationStatus.VERIFIED - User is 18+ (verified adult)
// - AgeSignalsVerificationStatus.SUPERVISED - User is supervised (13-17 with parental controls)
// - AgeSignalsVerificationStatus.SUPERVISED_APPROVAL_PENDING - Pending approval
// - AgeSignalsVerificationStatus.SUPERVISED_APPROVAL_DENIED - Approval denied
// - AgeSignalsVerificationStatus.UNKNOWN - User status unknown

// val fakeVerifiedUser = AgeSignalsResult.builder()
// .setUserStatus(AgeSignalsVerificationStatus.SUPERVISED)
// .setAgeLower(13)
// .setAgeUpper(17)
// .setMostRecentApprovalDate(
// Date.from(LocalDate.of(2025, 2, 1).atStartOfDay(ZoneOffset.UTC).toInstant())
// )
// .setInstallId("fake_install_id_12345")
// .build()

// val manager = FakeAgeSignalsManager()
// manager.setNextAgeSignalsResult(fakeVerifiedUser)

// Comment the line below to use the FakeAgeSignalsManager instead of the real API
val manager = AgeSignalsManagerFactory.create(appContext)

// Retrieve the AgeSignalsManager (NOTE: Mocked scenarios will be handled by getManager)
val manager = getManager(appContext)
val request = AgeSignalsRequest.builder().build()

val result = suspendCancellableCoroutine<PlayAgeRangeDeclarationResult> { cont ->
Expand Down Expand Up @@ -124,4 +101,66 @@ class PlayAgeRangeDeclaration : HybridPlayAgeRangeDeclarationSpec() {
)
}
}

// MOCK: Use setMockUser for testing
// https://developer.android.com/google/play/age-signals/test-age-signals-api
//
// To test different scenarios, you can override the userStatus with one of the following values:
// - AgeSignalsVerificationStatus.VERIFIED - User is 18+ (verified adult)
// - AgeSignalsVerificationStatus.SUPERVISED - User is supervised (13-17 with parental controls)
// - AgeSignalsVerificationStatus.SUPERVISED_APPROVAL_PENDING - Pending approval
// - AgeSignalsVerificationStatus.SUPERVISED_APPROVAL_DENIED - Approval denied
// - AgeSignalsVerificationStatus.UNKNOWN - User status unknown
//
// Configure it in any of your native app files (f.ex MainActivity.kt):
// TODO: Expose the setMockUser functionality to the native bridge
//
// import com.margelo.nitro.playagerangedeclaration.PlayAgeRangeDeclaration
// import com.margelo.nitro.playagerangedeclaration.PlayAgeRangeMockConfig
//
// override fun onCreate() {
// PlayAgeRangeDeclaration.setMockUser(
// PlayAgeRangeMockConfig(
// userStatus = AgeSignalsVerificationStatus.SUPERVISED,
// ageLower = 13,
// ageUpper = 17,
// mostRecentApprovalDate = Date.from(LocalDate.of(2025, 2, 1).atStartOfDay(ZoneOffset.UTC).toInstant()),
// installId = "fake_install_id_12345"
// )
// )
// }

// Companion object for managing age signal mocking
companion object {
var mockUser: AgeSignalsResult? = null

// Returns AgeSignalManager or the FakeAgeSignalsManager when a mock user is set
fun getManager(context: Context): AgeSignalsManager {
return mockUser?.let {
FakeAgeSignalsManager().apply { setNextAgeSignalsResult(it) }
} ?: AgeSignalsManagerFactory.create(context)
}

// Configure a mocked user
fun setMockUser(config: PlayAgeRangeMockConfig?) {
// Reset mock if no configuration is provided
if(config == null) {
mockUser = null
return
}

// Initialize the mocked user builder
val user = AgeSignalsResult.builder().setInstallId("fake_install_id_12345")

// Apply configuration properties to the builder
config.userStatus.let { user.setUserStatus(it) }
config.ageLower?.let { user.setAgeLower(it) }
config.ageUpper?.let { user.setAgeUpper(it) }
config.installId?.let { user.setInstallId(it) }
config.mostRecentApprovalDate?.let { user.setMostRecentApprovalDate(it) }

// Finalize and store the mocked user instance
mockUser = user.build()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.margelo.nitro.playagerangedeclaration

import com.google.android.play.agesignals.model.AgeSignalsVerificationStatus
import java.util.Date

data class PlayAgeRangeMockConfig(
// Required fields
@AgeSignalsVerificationStatus val userStatus: Int,

// Optional fields
val ageLower: Int? = null,
val ageUpper: Int? = null,
val installId: String? = null,
val mostRecentApprovalDate: Date? = null,
)
27 changes: 25 additions & 2 deletions src/PlayAgeRangeDeclaration.nitro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const PlayAgeRangeDeclarationUserStatus = {
UNKNOWN: '4',
} as const;

// https://developer.android.com/google/play/age-signals/use-age-signals-api#age-signals-responses
export const PlayAgeRangeDeclarationUserStatusString: Record<string, string> = {
[PlayAgeRangeDeclarationUserStatus.VERIFIED]: 'VERIFIED',
[PlayAgeRangeDeclarationUserStatus.SUPERVISED]: 'SUPERVISED',
Expand All @@ -18,19 +19,41 @@ export const PlayAgeRangeDeclarationUserStatusString: Record<string, string> = {
[PlayAgeRangeDeclarationUserStatus.UNKNOWN]: 'UNKNOWN',
};

export type PlayAgeRangeDeclarationUserStatusValues =
(typeof PlayAgeRangeDeclarationUserStatus)[keyof typeof PlayAgeRangeDeclarationUserStatus];

export interface PlayAgeRangeDeclarationResult {
isEligible: boolean;
installId?: string;
userStatus?: string;
userStatus?: PlayAgeRangeDeclarationUserStatusValues;
error?: string;
ageLower?: number;
ageUpper?: number;
mostRecentApprovalDate?: string; // ISO 8601 format (YYYY-MM-DD)
}

// https://developer.apple.com/documentation/declaredagerange/agerangeservice/agerangedeclaration#Determining-the-age-set-method
export const AppleAgeRangeDeclarationUserStatus = {
checkedByOtherMethod: 'checkedByOtherMethod',
governmentIDChecked: 'governmentIDChecked',
guardianCheckedByOtherMethod: 'guardianCheckedByOtherMethod',
guardianDeclared: 'guardianDeclared',
guardianGovernmentIDChecked: 'guardianGovernmentIDChecked',
guardianPaymentChecked: 'guardianPaymentChecked',
paymentChecked: 'paymentChecked',
selfDeclared: 'selfDeclared',

// Library defined statuses
declined: 'declined', // Declined sharing age range
unknown: 'unknown', // Fallback value
} as const;

export type AppleAgeRangeDeclarationUserStatusValues =
(typeof AppleAgeRangeDeclarationUserStatus)[keyof typeof AppleAgeRangeDeclarationUserStatus];

export interface DeclaredAgeRangeResult {
isEligible: boolean;
status?: string;
status?: AppleAgeRangeDeclarationUserStatusValues;
parentControls?: string;
lowerBound?: number;
upperBound?: number;
Expand Down
Loading