security_section_widget.dart 9.9 KB

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