security_section_widget.dart 9.8 KB

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