security_section_widget.dart 9.7 KB

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