validateAndroid static method
Validate Android configuration
Implementation
static List<VerificationResult> validateAndroid(
PlatformConfig localConfig,
ProjectConfig ulinkConfig,
) {
final results = <VerificationResult>[];
// Package Name match
if (localConfig.packageName != null &&
ulinkConfig.androidPackageName != null) {
if (localConfig.packageName != ulinkConfig.androidPackageName) {
results.add(
VerificationResult(
checkName: 'Android Package Name Match',
status: VerificationStatus.error,
message: 'Package name mismatch',
fixSuggestion: 'Update ULink config: ${localConfig.packageName}\n'
'Or update AndroidManifest.xml: ${ulinkConfig.androidPackageName}',
details: {
'local': localConfig.packageName,
'ulink': ulinkConfig.androidPackageName,
},
),
);
} else {
results.add(
VerificationResult(
checkName: 'Android Package Name Match',
status: VerificationStatus.success,
message: 'Package name matches',
),
);
}
} else {
results.add(
VerificationResult(
checkName: 'Android Package Name Match',
status: VerificationStatus.warning,
message: 'Package name not found in local config or ULink config',
fixSuggestion:
'Ensure package name is set in both AndroidManifest.xml and ULink dashboard',
),
);
}
// URL Scheme match - use Android-specific schemes for Flutter projects
// For pure Android projects, androidUrlSchemes should be set, but fallback to urlSchemes
// For Flutter projects, androidUrlSchemes contains only Android schemes
final androidSchemesToCheck = localConfig.androidUrlSchemes.isNotEmpty
? localConfig.androidUrlSchemes
: (localConfig.urlSchemes.isNotEmpty
? localConfig.urlSchemes
: <String>[]);
if (ulinkConfig.androidDeeplinkSchema != null &&
ulinkConfig.androidDeeplinkSchema!.isNotEmpty) {
final expectedScheme = ulinkConfig.androidDeeplinkSchema!
.replaceAll('://', '')
.toLowerCase();
final hasMatchingScheme = androidSchemesToCheck.any(
(scheme) => scheme.toLowerCase() == expectedScheme,
);
if (!hasMatchingScheme) {
// Provide more diagnostic info
final diagnosticMessage = androidSchemesToCheck.isEmpty
? 'No URL schemes found in AndroidManifest.xml. Check that intent filters have:\n'
' - <action android:name="android.intent.action.VIEW" />\n'
' - <category android:name="android.intent.category.BROWSABLE" /> or DEFAULT\n'
' - <data android:scheme="..." />'
: 'URL scheme mismatch';
results.add(
VerificationResult(
checkName: 'Android URL Scheme Match',
status: VerificationStatus.error,
message: diagnosticMessage,
fixSuggestion:
'Add URL scheme "$expectedScheme" to AndroidManifest.xml intent filter\n'
'Or update ULink config to match: ${androidSchemesToCheck.isEmpty ? "(none found)" : androidSchemesToCheck.join(", ")}',
details: {
'local': androidSchemesToCheck,
'ulink': expectedScheme,
'androidUrlSchemes': localConfig.androidUrlSchemes,
'urlSchemes': localConfig.urlSchemes,
},
),
);
} else {
// Check if there are extra schemes in local that don't match ULink
final extraSchemes = androidSchemesToCheck
.where((scheme) => scheme.toLowerCase() != expectedScheme)
.toList();
if (extraSchemes.isNotEmpty) {
results.add(
VerificationResult(
checkName: 'Android URL Scheme Match',
status: VerificationStatus.success,
message: 'URL scheme matches (ULink: $expectedScheme)',
details: {
'matched': expectedScheme,
'local': androidSchemesToCheck,
'ulink': expectedScheme,
},
),
);
results.add(
VerificationResult(
checkName: 'Android Extra URL Schemes',
status: VerificationStatus.warning,
message:
'Local has ${extraSchemes.length} extra URL scheme(s) not configured in ULink',
fixSuggestion:
'These schemes are in your AndroidManifest.xml but not in ULink config:\n${extraSchemes.join(", ")}\n'
'If these are intentional, you can ignore this warning. Otherwise, remove them or add "$expectedScheme" to ULink.',
details: {
'extraSchemes': extraSchemes,
'ulinkScheme': expectedScheme,
},
),
);
} else {
results.add(
VerificationResult(
checkName: 'Android URL Scheme Match',
status: VerificationStatus.success,
message: 'URL scheme matches',
),
);
}
}
} else if (androidSchemesToCheck.isNotEmpty) {
// Local has schemes but ULink doesn't have Android deeplink schema configured
results.add(
VerificationResult(
checkName: 'Android URL Scheme Match',
status: VerificationStatus.warning,
message:
'Local Android has URL schemes but ULink Android deeplink schema is not configured',
fixSuggestion:
'Configure Android deeplink schema in ULink dashboard to: ${androidSchemesToCheck.first}',
details: {
'local': androidSchemesToCheck,
'ulink': '(not configured)'
},
),
);
}
// App Link Host match - local needs to match at least ONE verified domain
final androidVerifiedDomains = ulinkConfig.domains
.where((d) => d.status == 'verified')
.map((d) => d.host)
.toList();
final allAndroidUlinkDomains =
ulinkConfig.domains.map((d) => d.host).toList();
if (localConfig.appLinkHosts.isNotEmpty) {
// Check if any local host matches a verified ULink domain
final matchedVerifiedHost = localConfig.appLinkHosts.firstWhere(
(localHost) => androidVerifiedDomains.contains(localHost),
orElse: () => '',
);
if (matchedVerifiedHost.isNotEmpty) {
// Found a match with a verified domain
results.add(
VerificationResult(
checkName: 'Android App Link Host Match',
status: VerificationStatus.success,
message: 'App Link host matches verified ULink domain',
details: {
'matched': matchedVerifiedHost,
'local': localConfig.appLinkHosts,
},
),
);
} else {
// No verified match, check if local host exists in ULink but not verified
final unverifiedMatch = localConfig.appLinkHosts.firstWhere(
(localHost) => allAndroidUlinkDomains.contains(localHost),
orElse: () => '',
);
if (unverifiedMatch.isNotEmpty) {
// Host exists in ULink but is not verified
final domainStatus = ulinkConfig.domains
.firstWhere((d) => d.host == unverifiedMatch)
.status;
results.add(
VerificationResult(
checkName: 'Android App Link Host Match',
status: VerificationStatus.error,
message:
'Domain "$unverifiedMatch" exists in ULink but is not verified (status: $domainStatus)',
fixSuggestion:
'Complete domain verification in ULink dashboard for "$unverifiedMatch"',
details: {
'local': localConfig.appLinkHosts,
'ulink': unverifiedMatch,
'status': domainStatus,
},
),
);
} else {
// Local host doesn't exist in ULink at all
results.add(
VerificationResult(
checkName: 'Android App Link Host Match',
status: VerificationStatus.error,
message:
'Local host "${localConfig.appLinkHosts.first}" not found in ULink',
fixSuggestion:
'Add domain "${localConfig.appLinkHosts.first}" to ULink dashboard and verify it',
details: {
'local': localConfig.appLinkHosts,
'ulink': allAndroidUlinkDomains,
},
),
);
}
}
} else if (androidVerifiedDomains.isNotEmpty) {
// ULink has verified domains but local doesn't have any
results.add(
VerificationResult(
checkName: 'Android App Link Host Match',
status: VerificationStatus.warning,
message: 'No App Link hosts in local config',
fixSuggestion:
'Add at least one verified domain to your AndroidManifest.xml:\n${androidVerifiedDomains.map((h) => ' android:host="$h"').join('\n')}',
details: {
'local': localConfig.appLinkHosts,
'ulink': androidVerifiedDomains,
},
),
);
} else if (ulinkConfig.domains.isNotEmpty) {
// ULink has domains but none are verified
results.add(
VerificationResult(
checkName: 'Android App Link Host Match',
status: VerificationStatus.warning,
message: 'ULink has domains but none are verified',
fixSuggestion:
'Verify your domains in the ULink dashboard to enable App Links',
details: {
'local': localConfig.appLinkHosts,
'ulink': ulinkConfig.domains
.map((d) => '${d.host} (${d.status})')
.toList(),
},
),
);
}
// SHA-256 Fingerprints check
if (ulinkConfig.androidSha256Fingerprints.isEmpty) {
results.add(
VerificationResult(
checkName: 'Android SHA-256 Fingerprints',
status: VerificationStatus.warning,
message: 'SHA-256 fingerprints not configured in ULink',
fixSuggestion:
'Add your app signing key SHA-256 fingerprints to ULink project configuration',
),
);
} else {
results.add(
VerificationResult(
checkName: 'Android SHA-256 Fingerprints',
status: VerificationStatus.success,
message:
'SHA-256 fingerprints are configured (${ulinkConfig.androidSha256Fingerprints.length} fingerprints)',
),
);
}
return results;
}