detectMinSdkAndroid function
Like minSdk but additionally returns the human-readable label of
the pattern that matched. Used by doctor to keep its report aligned
with the generation pipeline's actual detection logic.
Implementation
Future<MinSdkDetection> detectMinSdkAndroid({String prefixPath = '.'}) async {
final gradleFile = await findAndroidGradleFile(prefixPath);
final localPropertiesFile = File(
path.join(prefixPath, constants.androidLocalPropertiesFile),
);
if (gradleFile == null) {
final fromLocalProps = await _getMinSdkFromLocalProperties(
localPropertiesFile,
);
return MinSdkDetection(value: fromLocalProps, matchedLabel: null);
}
final isKts = gradleFile.path.endsWith('.kts');
final patterns = isKts ? _ktsMinSdkPatterns : _groovyMinSdkPatterns;
final content = await gradleFile.readAsString();
for (final p in patterns) {
final match = p.regex.firstMatch(content);
if (match == null) {
continue;
}
if (p.recurseToFlutter) {
final fromFlutterGradle = await _getMinSdkFromFlutterSdkGradle(
localPropertiesFile,
);
if (fromFlutterGradle != null) {
return MinSdkDetection(value: fromFlutterGradle, matchedLabel: p.label);
}
final fromLocalProps = await _getMinSdkFromLocalProperties(
localPropertiesFile,
);
// Even if the recurse failed to land a value, the pattern *did*
// match; preserve the label so doctor can report the indirection.
return MinSdkDetection(value: fromLocalProps, matchedLabel: p.label);
}
final captured = match.group(1);
if (captured == null) {
continue;
}
final parsed = int.tryParse(captured);
if (parsed != null) {
return MinSdkDetection(value: parsed, matchedLabel: p.label);
}
}
// No app-level pattern matched. Try local.properties as a last-ditch
// effort. No `matchedLabel` because no gradle pattern matched.
final fromLocalProps = await _getMinSdkFromLocalProperties(
localPropertiesFile,
);
return MinSdkDetection(value: fromLocalProps, matchedLabel: null);
}