diff --git a/mobile/lib/features/settings/settings_page.dart b/mobile/lib/features/settings/settings_page.dart index c3738d332..8975633db 100644 --- a/mobile/lib/features/settings/settings_page.dart +++ b/mobile/lib/features/settings/settings_page.dart @@ -117,8 +117,8 @@ class SettingsPage extends HookConsumerWidget { title: 'Share Crash Reports', subtitle: diagnostics.isConfigured ? 'Send crash details to help improve Buzz. Reports ' - 'exclude message text, screenshots, and personal ' - 'information.' + 'do not include screenshots, view hierarchy, ' + 'breadcrumbs, or performance traces.' : 'Crash reporting is unavailable in this build.', subtitleMaxLines: 4, trailing: Switch.adaptive( diff --git a/mobile/lib/shared/diagnostics/diagnostics_controller.dart b/mobile/lib/shared/diagnostics/diagnostics_controller.dart index 7ec561485..522a3d3da 100644 --- a/mobile/lib/shared/diagnostics/diagnostics_controller.dart +++ b/mobile/lib/shared/diagnostics/diagnostics_controller.dart @@ -70,6 +70,7 @@ class DiagnosticsController extends ChangeNotifier { } final previousConsent = _consentGranted; + var consentPersisted = false; _consentGranted = granted; notifyListeners(); try { @@ -80,8 +81,15 @@ class DiagnosticsController extends ChangeNotifier { if (!persisted) { throw StateError('Failed to persist diagnostics consent'); } + consentPersisted = true; await _applyCurrentConsent(); } on Object { + // A persisted revocation must survive teardown failure. Otherwise the + // next launch could initialize crash reporting against the user's + // explicit choice. + if (!granted && consentPersisted) { + rethrow; + } _consentGranted = previousConsent; final rolledBack = await _preferences.setBool( diagnosticsConsentPreferenceKey, diff --git a/mobile/test/features/settings/settings_page_test.dart b/mobile/test/features/settings/settings_page_test.dart index aeceebb13..3d04a9d95 100644 --- a/mobile/test/features/settings/settings_page_test.dart +++ b/mobile/test/features/settings/settings_page_test.dart @@ -56,6 +56,15 @@ void main() { expect(find.text('DIAGNOSTICS'), findsOneWidget); expect(find.text('Share Crash Reports'), findsOneWidget); + expect( + find.text( + 'Send crash details to help improve Buzz. Reports do not include ' + 'screenshots, view hierarchy, breadcrumbs, or performance traces.', + ), + findsOneWidget, + ); + expect(find.textContaining('personal information'), findsNothing); + expect(find.textContaining('message text'), findsNothing); final toggle = tester.widget(find.byType(Switch)); expect(toggle.value, isFalse); diff --git a/mobile/test/shared/diagnostics/diagnostics_controller_test.dart b/mobile/test/shared/diagnostics/diagnostics_controller_test.dart index e9c7d227d..09712430c 100644 --- a/mobile/test/shared/diagnostics/diagnostics_controller_test.dart +++ b/mobile/test/shared/diagnostics/diagnostics_controller_test.dart @@ -174,7 +174,7 @@ void main() { expect(reporter.initializeCalls, 1); }); - test('close failure rolls back revocation', () async { + test('close failure preserves persisted revocation', () async { final controller = await createController( storedValues: {diagnosticsConsentPreferenceKey: true}, ); @@ -183,8 +183,8 @@ void main() { await expectLater(controller.setConsent(false), throwsA(isA())); - expect(controller.consentGranted, isTrue); - expect(preferences.getBool(diagnosticsConsentPreferenceKey), isTrue); + expect(controller.consentGranted, isFalse); + expect(preferences.getBool(diagnosticsConsentPreferenceKey), isFalse); expect(reporter.closeCalls, 1); });