Skip to content

Commit

Permalink
Disable screenshots when encryption is enabled
Browse files Browse the repository at this point in the history
Close #17
  • Loading branch information
proninyaroslav committed Oct 9, 2024
1 parent 85e80ac commit 3044ece
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 6 deletions.
2 changes: 2 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
android:allowBackup="true"
android:fullBackupOnly="true">

<property android:name="REQUIRE_SECURE_ENV" android:value="1" />

<activity
android:name="MainActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
Expand Down
40 changes: 40 additions & 0 deletions lib/core/window_manager.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (C) 2024 Yaroslav Pronin <[email protected]>
//
// This file is part of Blink Comparison.
//
// Blink Comparison is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Blink Comparison is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Blink Comparison. If not, see <http://www.gnu.org/licenses/>.

import 'package:flutter_windowmanager_plus/flutter_windowmanager_plus.dart';
import 'package:injectable/injectable.dart';

abstract class WindowManager {
/// Allow/disallow screenshots
Future<bool> setSecureFlag(bool enable);
}

@Singleton(as: WindowManager)
class WindowManagerImpl implements WindowManager {
@override
Future<bool> setSecureFlag(bool enable) async {
if (enable) {
return FlutterWindowManagerPlus.addFlags(
FlutterWindowManagerPlus.FLAG_SECURE,
);
} else {
return FlutterWindowManagerPlus.clearFlags(
FlutterWindowManagerPlus.FLAG_SECURE,
);
}
}
}
2 changes: 2 additions & 0 deletions lib/injector.config.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import 'package:blink_comparison/core/crash_catcher/handler/notification_crash_handler.dart';
import 'package:blink_comparison/core/settings/app_settings.dart';
import 'package:blink_comparison/core/window_manager.dart';
import 'package:blink_comparison/core/workmanager/thumbnails_migrator_worker.dart';
import 'package:blink_comparison/core/workmanager/workmanager.dart';
import 'package:blink_comparison/ui/model/app_cubit.dart';
Expand Down Expand Up @@ -50,7 +51,10 @@ Future<void> _main() async {

runApp(
BlocProvider(
create: (context) => AppCubit(getIt<AppSettings>()),
create: (context) => AppCubit(
getIt<AppSettings>(),
getIt<WindowManager>(),
),
child: App(
enableDevicePreview: false,
navigatorKey: _navigatorKey,
Expand Down
21 changes: 18 additions & 3 deletions lib/ui/model/app_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,19 @@
import 'dart:async';

import 'package:blink_comparison/core/settings/app_settings.dart';
import 'package:blink_comparison/core/window_manager.dart';
import 'package:blink_comparison/ui/model/app_state.dart';
import 'package:bloc/bloc.dart';

class AppCubit extends Cubit<AppState> {
final AppSettings _pref;
final WindowManager _windowManager;
late final StreamSubscription subscription;

AppCubit(this._pref) : super(const AppState.initial());
AppCubit(
this._pref,
this._windowManager,
) : super(const AppState.initial());

Future<void> load() async {
emit(AppState.loaded(
Expand All @@ -43,7 +48,7 @@ class AppCubit extends Cubit<AppState> {
case AppSettingsKey.cameraFullscreenMode:
setCameraFullscreenMode(await _pref.cameraFullscreenMode);
case AppSettingsKey.encryptionPreference:
setEncryptPreference(_pref.encryptionPreferenceSync);
await setEncryptPreference(_pref.encryptionPreferenceSync);
}
});
}
Expand Down Expand Up @@ -93,7 +98,7 @@ class AppCubit extends Cubit<AppState> {
}
}

void setEncryptPreference(EncryptionPreference? value) {
Future<void> setEncryptPreference(EncryptionPreference? value) async {
if (state
case AppState(
:final theme?,
Expand All @@ -106,6 +111,16 @@ class AppCubit extends Cubit<AppState> {
cameraFullscreenMode: cameraFullscreenMode,
encrypt: value,
));
await _switchWindowSecureFlag(value);
}
}

Future<void> _switchWindowSecureFlag(EncryptionPreference? preference) async {
switch (preference) {
case null || EncryptionPreferenceNone():
await _windowManager.setSecureFlag(false);
case EncryptionPreferencePassword():
await _windowManager.setSecureFlag(true);
}
}
}
10 changes: 9 additions & 1 deletion pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_windowmanager_plus:
dependency: "direct main"
description:
name: flutter_windowmanager_plus
sha256: "4e2bf7c7f374699fd74d59785f1d74efd40052c24a5edde5a4d825cc72608d40"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
fluttertoast:
dependency: "direct main"
description:
Expand Down Expand Up @@ -1721,5 +1729,5 @@ packages:
source: hosted
version: "3.1.2"
sdks:
dart: ">=3.5.0 <4.0.0"
dart: ">=3.5.1 <4.0.0"
flutter: ">=3.24.0"
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ dependencies:
dynamic_color: ^1.7.0
material_symbols_icons: ^4.2785.1
shimmer_animation: ^2.2.1
flutter_windowmanager_plus: ^1.0.1

dev_dependencies:
analyzer: ^6.7.0
Expand Down
21 changes: 21 additions & 0 deletions test/mock/mock_window_manager.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (C) 2024 Yaroslav Pronin <[email protected]>
//
// This file is part of Blink Comparison.
//
// Blink Comparison is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Blink Comparison is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Blink Comparison. If not, see <http://www.gnu.org/licenses/>.

import 'package:blink_comparison/core/window_manager.dart';
import 'package:mocktail/mocktail.dart';

class MockWindowManager extends Mock implements WindowManager {}
55 changes: 54 additions & 1 deletion test/ui/app_cubit_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,22 @@ import 'dart:async';
import 'dart:ui';

import 'package:blink_comparison/core/settings/app_settings.dart';
import 'package:blink_comparison/core/window_manager.dart';
import 'package:blink_comparison/ui/model/app_cubit.dart';
import 'package:blink_comparison/ui/model/app_state.dart';
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';

import '../mock/mock.dart';
import '../mock/mock_window_manager.dart';

void main() {
group('AppCubit |', () {
late AppCubit cubit;
late AppSettings mockPref;
late StreamController<String> streamController;
late WindowManager mockWindowManager;

setUpAll(() {
streamController = StreamController.broadcast();
Expand All @@ -43,16 +46,19 @@ void main() {

setUp(() async {
mockPref = MockAppSettings();
mockWindowManager = MockWindowManager();
when(() => mockPref.theme).thenAnswer(
(_) async => const AppThemeType.system(),
);
when(() => mockPref.locale).thenAnswer(
(_) async => const AppLocaleType.system(),
);
when(() => mockPref.cameraFullscreenMode).thenAnswer((_) async => true);
when(() => mockPref.encryptionPreferenceSync)
.thenReturn(EncryptionPreference.none());
when(() => mockPref.changePrefStream())
.thenAnswer((_) => streamController.stream);
cubit = AppCubit(mockPref);
cubit = AppCubit(mockPref, mockWindowManager);
await cubit.load();
});

Expand Down Expand Up @@ -194,5 +200,52 @@ void main() {
),
],
);

blocTest(
'Change encryption preference',
build: () => cubit,
act: (AppCubit cubit) async {
when(() => mockWindowManager.setSecureFlag(any()))
.thenAnswer((_) async => true);
await cubit.setEncryptPreference(EncryptionPreference.password());
verify(() => mockWindowManager.setSecureFlag(true)).called(1);
await cubit.setEncryptPreference(EncryptionPreference.none());
verify(() => mockWindowManager.setSecureFlag(false)).called(1);
},
expect: () => [
const AppState.encryptPreferenceChanged(
theme: AppThemeType.system(),
locale: AppLocaleType.system(),
cameraFullscreenMode: true,
encrypt: EncryptionPreference.password(),
),
const AppState.encryptPreferenceChanged(
theme: AppThemeType.system(),
locale: AppLocaleType.system(),
cameraFullscreenMode: true,
encrypt: EncryptionPreference.none(),
),
],
);

blocTest(
'Listen encryption preference change',
build: () => cubit,
act: (AppCubit cubit) {
when(() => mockPref.encryptionPreferenceSync)
.thenReturn(EncryptionPreference.none());
when(() => mockWindowManager.setSecureFlag(any()))
.thenAnswer((_) async => true);
streamController.add(AppSettingsKey.encryptionPreference);
},
expect: () => [
const AppState.encryptPreferenceChanged(
theme: AppThemeType.system(),
locale: AppLocaleType.system(),
cameraFullscreenMode: true,
encrypt: EncryptionPreference.none(),
),
],
);
});
}

0 comments on commit 3044ece

Please sign in to comment.