From bbde8dea02d89ded9d476a5150e7dbc1375134aa Mon Sep 17 00:00:00 2001 From: Torpstitution <48436965+Torpstitution@users.noreply.github.com> Date: Thu, 14 May 2020 13:10:09 +0200 Subject: [PATCH] Feature 381 (#521) * Added Privacy section * Removed underlines and added tests * Indent * Indent * Comment * Removed unused variable * Removed unused variable * Fix * Fix * Fix * Fix * fixes #381 * Indent * lines_longer_than_80_chars fix * lines_longer_than_80_chars fix --- .../privacy_information_screen.dart | 286 ++++++++++++++++++ .../settings_screens/settings_screen.dart | 67 ++-- pubspec.lock | 2 +- .../privacy_information_screen_test.dart | 83 +++++ 4 files changed, 416 insertions(+), 22 deletions(-) create mode 100644 lib/screens/settings_screens/privacy_information_screen.dart create mode 100644 test/screens/settings_screens/privacy_information_screen_test.dart diff --git a/lib/screens/settings_screens/privacy_information_screen.dart b/lib/screens/settings_screens/privacy_information_screen.dart new file mode 100644 index 000000000..1011f395c --- /dev/null +++ b/lib/screens/settings_screens/privacy_information_screen.dart @@ -0,0 +1,286 @@ +import 'package:flutter/material.dart'; +import 'package:weekplanner/style/custom_color.dart' as theme; +import 'package:weekplanner/widgets/giraf_app_bar_widget.dart'; + +/// Screen where the user see their legal rights +class PrivacyInformationScreen extends StatelessWidget { + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: GirafAppBar( + title: 'Privatlivsinformation', + ), + body: SingleChildScrollView( + child: Column( + children: [ + Container( + margin: const EdgeInsets.all(30.0), + padding: const EdgeInsets.all(10.0), + decoration: myBoxDecoration(), + child: RichText(text: const TextSpan( + style: TextStyle(color: theme.GirafColors.black, + fontFamily: 'Quicksand'), + children: [ + TextSpan( + text: 'Oplysninger om vores behandling ' + 'af dine personoplysninger mv.''\n' '\n', + style: TextStyle(fontSize: 20.0, + fontWeight: FontWeight.bold)), + TextSpan( + text: '1. Vi er den dataansvarlige ' + '– hvordan kontakter du os?' '\n' '\n', + style: TextStyle(fontSize: 30.00, + fontWeight: FontWeight.bold)), + TextSpan( + text: 'Girafs Venner er dataansvarlig for ' + 'behandlingen af de personoplysninger, ' + 'som vi har modtaget om dig. ' + 'Du finder vores kontaktoplysninger ' + 'nedenfor.' '\n' '\n' + ' \u2022 ' + 'Navn: Girafs Venner' + '\n' '\n' + ' \u2022 ' + 'Telefon: 40 89 21 56' + '\n' '\n' + ' \u2022 ' + 'CVR-nr.: 40519025' + '\n' '\n' + ' \u2022 ' + 'Mail: ulrik@cs.aau.dk' '\n' '\n', + style: TextStyle(fontSize: 20.0)), + TextSpan( + text: '2. Formålene med og retsgrundlaget for ' + 'behandlingen af dine ' + 'personoplysninger' '\n' '\n', + style: TextStyle(fontSize: 30.0, + fontWeight: FontWeight.bold)), + TextSpan( + text: 'Vi behandler dine ' + 'personoplysninger til følgende ' + 'formål:' '\n' '\n' + ' \u2022 ' 'Formålet ' + 'med behandlingen ' + 'af personlige oplysninger ' + 'er udelukkende' + ' at tilvejebringe et ' + 'kommunikationsværktøj til ' + 'autistiske børn, ' + 'og medarbejdere til den udbudte ' + 'institution. Behandlingen ' + 'er således kun med ' + 'formål at skabe en ' + 'personaliseret interaktion, ' + 'mellem systemet og ' + 'det enkelte barn, ' + 'ved at have en ' + 'systembruger.' '\n' '\n' + + ' \u2022 ' 'Legitime ' + 'interesser, ' + 'der forfølges med ' + 'behandlingen.' '\n' + '\n' + 'Som nævnt ovenfor ' + 'sker vores behandling ' + 'af dine personoplysninger ' + 'på baggrund af ' + 'interesseafvejningsreglen ' + 'i databeskyttelsesforordningens ' + 'artikel 6, stk. 1, litra f. ' + 'De legitime interesser, der ' + 'begrunder behandlingen, er ' + 'at hjælpe autistiske børn ' + 'med at kommunikere og skabe ' + 'struktur i hverdagen gennem ' + 'et kommunikationsværktøj.' '\n' + '\n', + style: TextStyle(fontSize: 20.0)), + TextSpan( + text: '3. Kategorier af ' + 'personoplysninger' '\n' '\n', + style: TextStyle(fontSize: 30.0, + fontWeight: FontWeight.bold)), + TextSpan( + text: 'Vi behandler følgende ' + 'kategorier af personoplysninger ' + 'om dig:' '\n' + 'Identifikationsoplysninger:' '\n' + '\n' + ' \u2022 ' 'Almindelige ' + 'personoplysninger' + '\n' '\n', + style: TextStyle(fontSize: 20.0)), + TextSpan( + text: '4. Hvor dine ' + 'personoplysninger stammer ' + 'fra' '\n' '\n', + style: TextStyle(fontSize: 30.0, + fontWeight: FontWeight.bold)), + TextSpan( + text: 'Personoplysningerne stammer ' + 'fra registreringen af brugeren ' + 'i applikationen.' '\n' '\n', + style: TextStyle(fontSize: 20.0)), + TextSpan( + text: '5. Opbevaring af ' + 'dine personoplysninger' '\n' + '\n', + style: TextStyle(fontSize: 30.0, + fontWeight: FontWeight.bold)), + TextSpan( + text: 'Personlige oplysninger ' + 'slettes 1 år efter, at ' + 'brugeren er erklæret inaktiv,' + ' hvilket afhængigt af ' + 'institutionen enten kan være et ' + 'aktivt valg foretaget af ' + 'institutionen eller som resultat ' + 'af manglende anvendelse af ' + 'systemet.' '\n' '\n', + style: TextStyle(fontSize: 20.0)), + TextSpan( + text: '6. Retten til at ' + 'trække samtykke tilbage' + '\n' '\n', + style: TextStyle(fontSize: 30.0, + fontWeight: FontWeight.bold)), + TextSpan( + text: 'Du har til enhver tid ret ' + 'til at trække dit samtykke tilbage. ' + 'Dette kan du gøre ved at kontakte ' + 'os på de kontaktoplysninger, der' + ' fremgår ovenfor i punkt 1. Hvis' + ' du vælger at trække dit samtykke' + ' tilbage, påvirker det ikke ' + 'lovligheden af vores behandling' + ' af dine personoplysninger på' + ' baggrund af dit tidligere ' + 'meddelte samtykke og op til tidspunktet' + ' for tilbagetrækningen. ' + 'Hvis du tilbagetrækker' + ' dit samtykke, har det ' + 'derfor først virkning ' + 'fra dette tidspunkt.' '\n' '\n', + style: TextStyle(fontSize: 20.0)), + TextSpan(text: '7. Dine rettigheder' '\n' '\n', + style: TextStyle(fontSize: 30.0, + fontWeight: FontWeight.bold)), + TextSpan( + text: 'Du har efter ' + 'databeskyttelsesforordningen en ' + 'række rettigheder i ' + 'forhold til vores behandling ' + 'af oplysninger om dig. ' + 'Hvis du vil gøre brug af ' + 'dine rettigheder skal ' + 'du kontakte os.' '\n' '\n' + ' \u2022 ' 'Ret til ' + 'at se oplysninger (indsigtsret)' + '\n' '\n' + ' \u2022 ' 'Du har ' + 'ret til at få indsigt' + ' i de oplysninger, ' + 'som vi behandler om dig, ' + 'samt en række yderligere ' + 'oplysninger.' '\n' '\n' + ' \u2022 ' 'Ret til ' + 'berigtigelse (rettelse). ' + 'Du har ret til at få urigtige ' + 'oplysninger om dig selv ' + 'rettet.' '\n' '\n', + style: TextStyle(fontSize: 20.0)), + TextSpan( + text: ' \u2022 ' + 'Ret til sletning. I særlige' + ' tilfælde har du ret til at ' + 'få slettet oplysninger om dig, ' + 'inden tidspunktet for vores ' + 'almindelige generelle ' + 'sletning indtræffer.' + '\n' '\n' + ' \u2022 ' + 'Ret til begrænsning af behandling. ' + 'Du har visse tilfælde ret til at få ' + 'behandlingen af dine personoplysninger ' + 'begrænset. Hvis du har ret ' + 'til at få begrænset behandlingen,' + ' må vi fremover kun behandle ' + 'oplysningerne – bortset fra opbevaring ' + '– med dit samtykke, eller med henblik ' + 'på at retskrav kan fastlægges, ' + 'gøres gældende eller forsvares, ' + 'eller for at beskytte en person' + ' eller vigtige ' + 'samfundsinteresser.' '\n' '\n' + ' \u2022 ' + 'Ret til indsigelse. ' + 'Du har i visse tilfælde ' + 'ret til at gøre indsigelse ' + 'mod vores eller lovlige ' + 'behandling af dine ' + 'personoplysninger. ' + 'Du kan også gøre indsigelse ' + 'mod behandling af dine ' + 'oplysninger til direkte ' + 'markedsføring.' '\n' '\n' + ' \u2022 ' + 'Ret til at transmittere ' + 'oplysninger (dataportabilitet). ' + 'Du har i visse tilfælde ' + 'ret til at modtage dine ' + 'personoplysninger i et ' + 'struktureret, almindeligt ' + 'anvendt og maskinlæsbart ' + 'format samt at få overført ' + 'disse personoplysninger ' + 'fra én dataansvarlig ' + 'til en anden uden ' + 'hindring.' '\n' '\n' + 'Du kan læse mere om dine ' + 'rettigheder i Datatilsynets' + ' vejledning om de ' + 'registreredes rettigheder, ' + 'som du finder på ' + 'www.datatilsynet.dk.' '\n' '\n', + style: TextStyle(fontSize: 20.0)), + TextSpan(text: '8. Klage til Datatilsynet' + '\n' '\n', + style: TextStyle(fontSize: 30.0, + fontWeight: FontWeight.bold)), + TextSpan( + text: 'Du har ret til at ' + 'indgive en klage til Datatilsynet, ' + 'hvis du er utilfreds med ' + 'den måde, vi behandler dine ' + 'personoplysninger på. ' + 'Du finder Datatilsynets ' + 'kontaktoplysninger på ' + 'www.datatilsynet.dk.' '\n' '\n', + style: TextStyle(fontSize: 20.0)), + + + ], + ), + )) + ]) + )); + } + +/// Box to make it look nicer + BoxDecoration myBoxDecoration() { + return BoxDecoration( + border: Border.all( + color: theme.GirafColors.appBarOrange, + // + width: 5, // + ), + borderRadius: const BorderRadius.all( + Radius.circular(15.0) // <--- border radius here + ), + ); + } +} + + diff --git a/lib/screens/settings_screens/settings_screen.dart b/lib/screens/settings_screens/settings_screen.dart index 112f08471..ca3f4da74 100644 --- a/lib/screens/settings_screens/settings_screen.dart +++ b/lib/screens/settings_screens/settings_screen.dart @@ -7,6 +7,7 @@ import 'package:weekplanner/blocs/settings_bloc.dart'; import 'package:weekplanner/routes.dart'; import 'package:weekplanner/screens/settings_screens/number_of_days_selection_screen.dart'; import 'package:weekplanner/screens/settings_screens/color_theme_selection_screen.dart'; +import 'package:weekplanner/screens/settings_screens/privacy_information_screen.dart'; import 'package:weekplanner/screens/settings_screens/time_representation_screen.dart'; import 'package:weekplanner/widgets/giraf_app_bar_widget.dart'; import 'package:weekplanner/widgets/settings_widgets/settings_section.dart'; @@ -14,7 +15,6 @@ import 'package:weekplanner/widgets/settings_widgets/settings_section_arrow_butt import 'package:weekplanner/widgets/settings_widgets/settings_section_checkboxButton.dart'; import 'package:weekplanner/widgets/settings_widgets/settings_section_item.dart'; import 'package:weekplanner/widgets/settings_widgets/settings_theme_display_box.dart'; - import '../../di.dart'; import '../../widgets/settings_widgets/settings_section_arrow_button.dart'; import 'completed_activity_icon_selection_screen.dart'; @@ -45,7 +45,8 @@ class SettingsScreen extends StatelessWidget { _buildWeekPlanSection(context), _buildTimerSection(context), _buildTimeRepresentationSettings(context), - _buildUserSettings() + _buildUserSettings(), + _buildPrivacySection() ], ); } @@ -60,24 +61,28 @@ class SettingsScreen extends StatelessWidget { return SettingsSection('Tema', [ SettingsArrowButton( 'Farver på ugeplan', - () => Routes.push( - context, ColorThemeSelectorScreen(user: _user)) - .then( - (Object object) => _settingsBloc.loadSettings(_user)), + () => + Routes.push( + context, ColorThemeSelectorScreen(user: _user)) + .then( + (Object object) => + _settingsBloc.loadSettings(_user)), titleTrailing: ThemeBox.fromHexValues( settingsModel.weekDayColors[0].hexColor, settingsModel.weekDayColors[1].hexColor)), SettingsArrowButton( 'Tegn for udførelse', - () => Routes.push(context, CompletedActivityIconScreen(_user)) - .then( - (Object object) => _settingsBloc.loadSettings(_user)), + () => Routes.push(context, + CompletedActivityIconScreen(_user)) + .then( + (Object object) => + _settingsBloc.loadSettings(_user)), titleTrailing: Text(settingsModel.completeMark == - CompleteMark.Checkmark + CompleteMark.Checkmark ? 'Flueben' : settingsModel.completeMark == CompleteMark.MovedRight - ? 'Lav aktiviteten grå' - : 'Fjern aktiviteten')) + ? 'Lav aktiviteten grå' + : 'Fjern aktiviteten')) ]); } else { return const Center( @@ -103,13 +108,14 @@ class SettingsScreen extends StatelessWidget { return SettingsSection('Ugeplan', [ SettingsArrowButton( 'Antal dage', - () => Routes.push(context, NumberOfDaysScreen(_user)) - .then((Object object) => _settingsBloc.loadSettings(_user)), + () => Routes.push(context, NumberOfDaysScreen(_user)) + .then((Object object) => + _settingsBloc.loadSettings(_user)), titleTrailing: Text(settingsModel.nrOfDaysToDisplay == 1 ? 'En dag' : settingsModel.nrOfDaysToDisplay == 5 - ? 'Mandag til fredag' - : 'Mandag til søndag'), + ? 'Mandag til fredag' + : 'Mandag til søndag'), ), SettingsCheckMarkButton.fromBoolean( settingsModel.pictogramText, 'Piktogram tekst er synlig', () { @@ -142,7 +148,7 @@ class SettingsScreen extends StatelessWidget { SettingsCheckMarkButton.fromBoolean( _settingsModel.lockTimerControl, 'Lås tidsstyring', () { _settingsModel.lockTimerControl = - !_settingsModel.lockTimerControl; + !_settingsModel.lockTimerControl; _settingsBloc .updateSettings(_user.id, _settingsModel) .listen((SettingsModel response) { @@ -164,6 +170,24 @@ class SettingsScreen extends StatelessWidget { ]); } + Widget _buildPrivacySection() { + return StreamBuilder( + stream: _settingsBloc.settings, + builder: (BuildContext context, + AsyncSnapshot settingsSnapshot) { + return SettingsSection('Privatliv', [ + SettingsArrowButton( + 'Privatlivsinformationer', + () => + Routes.push(context, PrivacyInformationScreen()) + .then((Object object) => + _settingsBloc.loadSettings(_user)), + ), + ]); + + }); + } + Widget _buildTimeRepresentationSettings(BuildContext context) { return StreamBuilder( stream: _settingsBloc.settings, @@ -175,16 +199,17 @@ class SettingsScreen extends StatelessWidget { return SettingsSection('Tidsrepræsentation', [ SettingsArrowButton( 'Indstillinger for tidsrepræsentation', - () => Routes.push(context, TimeRepresentationScreen(_user)) - .then((Object object) => _settingsBloc.loadSettings(_user)), + () => Routes.push(context, TimeRepresentationScreen(_user)) + .then((Object object) => + _settingsBloc.loadSettings(_user)), titleTrailing: Image( width: 50, height: 50, image: AssetImage(userTimer == DefaultTimer.PieChart ? 'assets/timer/piechart_icon.png' : userTimer == DefaultTimer.Hourglass - ? 'assets/timer/hourglass_icon.png' - : 'assets/timer/countdowntimer_icon.png')), + ? 'assets/timer/hourglass_icon.png' + : 'assets/timer/countdowntimer_icon.png')), ) ]); } else { diff --git a/pubspec.lock b/pubspec.lock index 5f9493908..4abda5632 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -405,4 +405,4 @@ packages: version: "3.5.0" sdks: dart: ">=2.7.0 <3.0.0" - flutter: ">=1.12.13 <2.0.0" + flutter: ">=1.12.13+hotfix.5 <2.0.0" diff --git a/test/screens/settings_screens/privacy_information_screen_test.dart b/test/screens/settings_screens/privacy_information_screen_test.dart new file mode 100644 index 000000000..c3fdc2192 --- /dev/null +++ b/test/screens/settings_screens/privacy_information_screen_test.dart @@ -0,0 +1,83 @@ +import 'package:api_client/api/api.dart'; +import 'package:api_client/api/user_api.dart'; +import 'package:api_client/models/enums/role_enum.dart'; +import 'package:api_client/models/settings_model.dart'; +import 'package:flutter/material.dart'; +import 'package:api_client/models/giraf_user_model.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:rxdart/rxdart.dart'; +import 'package:weekplanner/blocs/auth_bloc.dart'; +import 'package:weekplanner/blocs/settings_bloc.dart'; +import 'package:weekplanner/blocs/toolbar_bloc.dart'; +import 'package:weekplanner/di.dart'; +import 'package:weekplanner/screens/settings_screens/privacy_information_screen.dart'; +import 'package:weekplanner/widgets/giraf_app_bar_widget.dart'; + +SettingsModel mockSettings; + +class MockUserApi extends Mock implements UserApi { + @override + Observable me() { + return Observable.just( + GirafUserModel(id: '1', username: 'test', role: Role.Guardian)); + } + + @override + Observable getSettings(String id) { + return Observable.just(mockSettings); + } +} + +void main() { + Api api; + SettingsBloc settingsBloc; + + setUp(() { + api = Api('any'); + api.user = MockUserApi(); + settingsBloc = SettingsBloc(api); + + mockSettings = SettingsModel( + orientation: null, + completeMark: null, + cancelMark: null, + defaultTimer: null, + theme: null, + nrOfDaysToDisplay: 1, + pictogramText: false, + lockTimerControl: false); + + when(api.user.updateSettings(any, any)).thenAnswer((_) { + return Observable.just(mockSettings); + }); + + di.clearAll(); + di.registerDependency((_) => AuthBloc(api)); + di.registerDependency((_) => ToolbarBloc()); + di.registerDependency((_) => settingsBloc); + }); + + testWidgets('Renders PrivacyInformationScreen', (WidgetTester tester) async { + await tester + .pumpWidget(MaterialApp(home: PrivacyInformationScreen())); + expect(find.byType(PrivacyInformationScreen), findsOneWidget); + }); + + testWidgets('Has GirafAppBar', (WidgetTester tester) async { + await tester.pumpWidget(MaterialApp(home: PrivacyInformationScreen())); + expect( + find.byWidgetPredicate((Widget widget) => + widget is GirafAppBar && widget.title == 'Privatlivsinformation'), + findsOneWidget); + expect(find.byType(GirafAppBar), findsOneWidget); + }); + + testWidgets('Has ScrollView', + (WidgetTester tester) async { + await tester + .pumpWidget(MaterialApp(home: PrivacyInformationScreen())); + await tester.pumpAndSettle(); + expect(find.byType(SingleChildScrollView), findsOneWidget); + }); +} \ No newline at end of file