security_section_widget.dart 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. import 'dart:async';
  2. import 'dart:io';
  3. import 'package:flutter/material.dart';
  4. import 'package:flutter_windowmanager/flutter_windowmanager.dart';
  5. import 'package:photos/core/configuration.dart';
  6. import 'package:photos/core/event_bus.dart';
  7. import 'package:photos/events/two_factor_status_change_event.dart';
  8. import 'package:photos/services/user_service.dart';
  9. import 'package:photos/ui/app_lock.dart';
  10. import 'package:photos/ui/loading_widget.dart';
  11. import 'package:photos/ui/sessions_page.dart';
  12. import 'package:photos/ui/settings/settings_section_title.dart';
  13. import 'package:photos/ui/settings/settings_text_item.dart';
  14. import 'package:photos/utils/auth_util.dart';
  15. import 'package:photos/utils/toast_util.dart';
  16. class SecuritySectionWidget extends StatefulWidget {
  17. SecuritySectionWidget({Key key}) : super(key: key);
  18. @override
  19. _SecuritySectionWidgetState createState() => _SecuritySectionWidgetState();
  20. }
  21. class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
  22. static const kAuthToViewSessions =
  23. "please authenticate to view your active sessions";
  24. final _config = Configuration.instance;
  25. StreamSubscription<TwoFactorStatusChangeEvent> _twoFactorStatusChangeEvent;
  26. @override
  27. void initState() {
  28. super.initState();
  29. _twoFactorStatusChangeEvent =
  30. Bus.instance.on<TwoFactorStatusChangeEvent>().listen((event) async {
  31. if (mounted) {
  32. setState(() {});
  33. }
  34. });
  35. }
  36. @override
  37. void dispose() {
  38. _twoFactorStatusChangeEvent.cancel();
  39. super.dispose();
  40. }
  41. @override
  42. Widget build(BuildContext context) {
  43. final List<Widget> children = [];
  44. children.addAll([
  45. SettingsSectionTitle("security"),
  46. ]);
  47. if (_config.hasConfiguredAccount()) {
  48. children.addAll(
  49. [
  50. Padding(padding: EdgeInsets.all(6)),
  51. SizedBox(
  52. height: 36,
  53. child: Row(
  54. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  55. children: [
  56. Text("two-factor"),
  57. FutureBuilder(
  58. future: UserService.instance.fetchTwoFactorStatus(),
  59. builder: (_, snapshot) {
  60. if (snapshot.hasData) {
  61. return Switch(
  62. value: snapshot.data,
  63. onChanged: (value) async {
  64. AppLock.of(context).setEnabled(false);
  65. final result = await requestAuthentication();
  66. AppLock.of(context).setEnabled(
  67. Configuration.instance.shouldShowLockScreen());
  68. if (!result) {
  69. showToast(
  70. "please authenticate to configure two-factor authentication");
  71. return;
  72. }
  73. if (value) {
  74. UserService.instance.setupTwoFactor(context);
  75. } else {
  76. _disableTwoFactor();
  77. }
  78. },
  79. );
  80. } else if (snapshot.hasError) {
  81. return Icon(
  82. Icons.error_outline,
  83. color: Colors.white.withOpacity(0.8),
  84. );
  85. }
  86. return loadWidget;
  87. },
  88. ),
  89. ],
  90. ),
  91. ),
  92. ],
  93. );
  94. }
  95. children.addAll([
  96. Padding(padding: EdgeInsets.all(2)),
  97. Divider(height: 4),
  98. Platform.isIOS
  99. ? Padding(padding: EdgeInsets.all(2))
  100. : Padding(padding: EdgeInsets.all(4)),
  101. SizedBox(
  102. height: 36,
  103. child: Row(
  104. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  105. children: [
  106. Text("lockscreen"),
  107. Switch(
  108. value: _config.shouldShowLockScreen(),
  109. onChanged: (value) async {
  110. AppLock.of(context).disable();
  111. final result = await requestAuthentication();
  112. if (result) {
  113. AppLock.of(context).setEnabled(value);
  114. _config.setShouldShowLockScreen(value);
  115. setState(() {});
  116. } else {
  117. AppLock.of(context)
  118. .setEnabled(_config.shouldShowLockScreen());
  119. }
  120. },
  121. ),
  122. ],
  123. ),
  124. ),
  125. ]);
  126. if (Platform.isAndroid) {
  127. children.addAll(
  128. [
  129. Padding(padding: EdgeInsets.all(4)),
  130. Divider(height: 4),
  131. Padding(padding: EdgeInsets.all(4)),
  132. SizedBox(
  133. height: 36,
  134. child: Row(
  135. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  136. children: [
  137. Text("hide from recents"),
  138. Switch(
  139. value: _config.shouldHideFromRecents(),
  140. onChanged: (value) async {
  141. if (value) {
  142. AlertDialog alert = AlertDialog(
  143. title: Text("hide from recents?"),
  144. content: SingleChildScrollView(
  145. child: Column(
  146. mainAxisAlignment: MainAxisAlignment.start,
  147. crossAxisAlignment: CrossAxisAlignment.start,
  148. children: const [
  149. Text(
  150. "hiding from the task switcher will prevent you from taking screenshots in this app.",
  151. style: TextStyle(
  152. height: 1.5,
  153. ),
  154. ),
  155. Padding(padding: EdgeInsets.all(8)),
  156. Text(
  157. "are you sure?",
  158. style: TextStyle(
  159. height: 1.5,
  160. ),
  161. ),
  162. ],
  163. ),
  164. ),
  165. actions: [
  166. TextButton(
  167. child: Text("no",
  168. style: TextStyle(color: Colors.white)),
  169. onPressed: () {
  170. Navigator.of(context, rootNavigator: true)
  171. .pop('dialog');
  172. },
  173. ),
  174. TextButton(
  175. child: Text("yes",
  176. style: TextStyle(
  177. color: Colors.white.withOpacity(0.8))),
  178. onPressed: () async {
  179. Navigator.of(context, rootNavigator: true)
  180. .pop('dialog');
  181. await _config.setShouldHideFromRecents(true);
  182. await FlutterWindowManager.addFlags(
  183. FlutterWindowManager.FLAG_SECURE);
  184. setState(() {});
  185. },
  186. ),
  187. ],
  188. );
  189. showDialog(
  190. context: context,
  191. builder: (BuildContext context) {
  192. return alert;
  193. },
  194. );
  195. } else {
  196. await _config.setShouldHideFromRecents(false);
  197. await FlutterWindowManager.clearFlags(
  198. FlutterWindowManager.FLAG_SECURE);
  199. setState(() {});
  200. }
  201. },
  202. ),
  203. ],
  204. ),
  205. ),
  206. ],
  207. );
  208. }
  209. children.addAll([
  210. Padding(padding: EdgeInsets.all(4)),
  211. Divider(height: 4),
  212. Padding(padding: EdgeInsets.all(2)),
  213. GestureDetector(
  214. behavior: HitTestBehavior.translucent,
  215. onTap: () async {
  216. AppLock.of(context).setEnabled(false);
  217. final result = await requestAuthentication(kAuthToViewSessions);
  218. AppLock.of(context)
  219. .setEnabled(Configuration.instance.shouldShowLockScreen());
  220. if (!result) {
  221. showToast(kAuthToViewSessions);
  222. return;
  223. }
  224. Navigator.of(context).push(
  225. MaterialPageRoute(
  226. builder: (BuildContext context) {
  227. return SessionsPage();
  228. },
  229. ),
  230. );
  231. },
  232. child: SettingsTextItem(
  233. text: "active sessions", icon: Icons.navigate_next),
  234. ),
  235. ]);
  236. return Column(
  237. children: children,
  238. );
  239. }
  240. void _disableTwoFactor() {
  241. AlertDialog alert = AlertDialog(
  242. title: Text("disable two-factor"),
  243. content:
  244. Text("are you sure you want to disable two-factor authentication?"),
  245. actions: [
  246. TextButton(
  247. child: Text(
  248. "no",
  249. style: TextStyle(
  250. color: Theme.of(context).buttonColor,
  251. ),
  252. ),
  253. onPressed: () {
  254. Navigator.of(context, rootNavigator: true).pop('dialog');
  255. },
  256. ),
  257. TextButton(
  258. child: Text(
  259. "yes",
  260. style: TextStyle(
  261. color: Colors.red,
  262. ),
  263. ),
  264. onPressed: () async {
  265. await UserService.instance.disableTwoFactor(context);
  266. Navigator.of(context, rootNavigator: true).pop('dialog');
  267. },
  268. ),
  269. ],
  270. );
  271. showDialog(
  272. context: context,
  273. builder: (BuildContext context) {
  274. return alert;
  275. },
  276. );
  277. }
  278. }