test(automotive): assert the picture-in-picture vetoes on every host
41ffaa7f2gated picture-in-picture on FEATURE_AUTOMOTIVE and added a settings case for it, but the assertion that case leads with — a stored auto-PiP true surviving a read — needs supportsPictureInPicture() to be true, and that gate ends in Platform.isAndroid || isIOS || isMacOS. The term is false and unmockable on the Linux and Windows runners, so the case passed on a macOS host and could never pass in CI: sanity checks have been red for six commits on this one failure out of 4723.f13f5af6erecorded it as a pre-existing Windows-host failure, but it entered in this window and is red on Linux too. Extract the gate's decision into a pure pictureInPictureAllowed that takes the host's own capability as a parameter, the way driver_distraction.dart already splits automotivePlaybackAllowed from its ambient wrapper. The boolean algebra is unchanged, so the three callers keep their behaviour; what changes is that the automotive and TV vetoes become observable where every Platform branch is false, instead of being vacuous on the host that gates the release. The settings case keeps the pref-level contract on both host classes: a stored true survives where the host supports PiP, and the gate pins it off where it does not. Verified with the host term forced false to emulate a Linux runner: both files stay green, as does the full suite on macOS.
This commit is contained in:
@@ -48,6 +48,25 @@ AndroidTvFeatureDetection detectAndroidTvFromSystemFeatures(Iterable<String> fea
|
||||
);
|
||||
}
|
||||
|
||||
/// Whether a floating player may be offered, given the host platform's own
|
||||
/// picture-in-picture capability and the detected form factor.
|
||||
///
|
||||
/// Cars commonly lack `FEATURE_PICTURE_IN_PICTURE`, and a floating player would
|
||||
/// keep the app's UI on screen while driving, which `DD-2` forbids. TV form
|
||||
/// factors have no windowed surface to float into.
|
||||
///
|
||||
/// [hostSupportsPictureInPicture] is injected rather than read from [Platform]
|
||||
/// so the form-factor vetoes stay observable on hosts that never support PiP:
|
||||
/// on the Linux and Windows CI runners every [Platform] branch of the real gate
|
||||
/// is false and unmockable, which would otherwise make the vetoes vacuous
|
||||
/// exactly where the release is gated.
|
||||
bool pictureInPictureAllowed({
|
||||
required bool hostSupportsPictureInPicture,
|
||||
required bool isAppleTv,
|
||||
required bool isTv,
|
||||
required bool isAutomotive,
|
||||
}) => hostSupportsPictureInPicture && !isAppleTv && !isTv && !isAutomotive;
|
||||
|
||||
/// Service for detecting if the app is running on Android TV or Apple TV.
|
||||
class TvDetectionService {
|
||||
static final AsyncSingleton<TvDetectionService> _singleton = AsyncSingleton();
|
||||
@@ -267,11 +286,12 @@ class PlatformDetector {
|
||||
return isAppleTV() || isDesktopOS() || (Platform.isAndroid && isTV());
|
||||
}
|
||||
|
||||
static bool supportsPictureInPicture() {
|
||||
// Cars commonly lack FEATURE_PICTURE_IN_PICTURE, and a floating player
|
||||
// would keep the app's UI on screen while driving, which `DD-2` forbids.
|
||||
return !isAppleTV() && !isTV() && !isAutomotive() && (Platform.isAndroid || Platform.isIOS || Platform.isMacOS);
|
||||
}
|
||||
static bool supportsPictureInPicture() => pictureInPictureAllowed(
|
||||
hostSupportsPictureInPicture: Platform.isAndroid || Platform.isIOS || Platform.isMacOS,
|
||||
isAppleTv: isAppleTV(),
|
||||
isTv: isTV(),
|
||||
isAutomotive: isAutomotive(),
|
||||
);
|
||||
|
||||
/// Detects if the device is likely a tablet based on screen size
|
||||
/// Uses diagonal screen size to determine if device is a tablet
|
||||
|
||||
@@ -236,7 +236,13 @@ void main() {
|
||||
final settings = await SettingsService.getInstance();
|
||||
await settings.write(SettingsService.autoPip, true);
|
||||
|
||||
expect(settings.read(SettingsService.autoPip), isTrue);
|
||||
// The pref can only surface a stored true where the host itself supports
|
||||
// PiP. That term is Platform.isAndroid/isIOS/isMacOS, unmockable and false
|
||||
// on the Linux and Windows CI hosts, where the gate pins the pref off no
|
||||
// matter what is stored. pictureInPictureAllowed covers the automotive
|
||||
// veto itself on every host.
|
||||
final hostSupportsPip = PlatformDetector.supportsPictureInPicture();
|
||||
expect(settings.read(SettingsService.autoPip), hostSupportsPip ? isTrue : isFalse);
|
||||
|
||||
TvDetectionService.debugSetAutomotiveOverride(true);
|
||||
|
||||
|
||||
@@ -113,4 +113,32 @@ void main() {
|
||||
expect(detection.isAutomotive, isFalse);
|
||||
});
|
||||
});
|
||||
|
||||
group('pictureInPictureAllowed', () {
|
||||
bool allowed({bool host = true, bool appleTv = false, bool tv = false, bool automotive = false}) =>
|
||||
pictureInPictureAllowed(
|
||||
hostSupportsPictureInPicture: host,
|
||||
isAppleTv: appleTv,
|
||||
isTv: tv,
|
||||
isAutomotive: automotive,
|
||||
);
|
||||
|
||||
test('a plain handheld host may float a player', () {
|
||||
expect(allowed(), isTrue);
|
||||
});
|
||||
|
||||
test('automotive vetoes a host that otherwise supports PiP', () {
|
||||
expect(allowed(automotive: true), isFalse);
|
||||
});
|
||||
|
||||
test('TV form factors veto a host that otherwise supports PiP', () {
|
||||
expect(allowed(tv: true), isFalse);
|
||||
expect(allowed(appleTv: true), isFalse);
|
||||
});
|
||||
|
||||
test('a host without PiP is never allowed, whatever the form factor', () {
|
||||
expect(allowed(host: false), isFalse);
|
||||
expect(allowed(host: false, automotive: true), isFalse);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user