Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 

447 lignes
18 KiB

  1. import 'dart:async';
  2. import 'dart:convert';
  3. import 'dart:io';
  4. import 'dart:ui' as ui;
  5. import 'package:flutter/cupertino.dart';
  6. import 'package:flutter/material.dart';
  7. import 'package:get/get.dart';
  8. import 'package:michele_s_application8/widgets/custom_button.dart';
  9. import 'package:michele_s_application8/widgets/custom_text_form_field.dart';
  10. import 'package:http/http.dart' as http;
  11. import 'package:michele_s_application8/presentation/camera_page/camera_page.dart';
  12. import 'package:flutter_dotenv/flutter_dotenv.dart';
  13. class LoginScreen extends StatefulWidget {
  14. @override
  15. _LoginScreenState createState() => _LoginScreenState();
  16. }
  17. class _LoginScreenState extends State<LoginScreen> {
  18. GlobalKey<FormState> _formKey = GlobalKey<FormState>();
  19. final TextEditingController _timeController1 = TextEditingController();
  20. final TextEditingController _titleController = TextEditingController();
  21. final TextEditingController _presenterNameController = TextEditingController();
  22. final TextEditingController _camNumberController = TextEditingController(text: '1');
  23. late TextEditingController _languageController;
  24. bool isRecording = false;
  25. bool isLoading = false;
  26. bool isError = false;
  27. bool showStartRecordingButton = true;
  28. Timer? _recordingTimer;
  29. @override
  30. void initState() {
  31. super.initState();
  32. String deviceLanguage = ui.window.locale.languageCode;
  33. String language = (deviceLanguage == 'en') ? 'EN' : (deviceLanguage == 'it') ? 'IT' : 'IT';
  34. _languageController = TextEditingController(text: language);
  35. }
  36. @override
  37. void dispose() {
  38. _recordingTimer?.cancel();
  39. super.dispose();
  40. }
  41. @override
  42. Widget build(BuildContext context) {
  43. return SafeArea(
  44. child: Scaffold(
  45. resizeToAvoidBottomInset: false,
  46. body: Container(
  47. decoration: BoxDecoration(
  48. gradient: LinearGradient(
  49. begin: Alignment.topLeft,
  50. end: Alignment.bottomRight,
  51. colors: [Colors.white, Colors.purple],
  52. ),
  53. ),
  54. child: Stack(
  55. children: [
  56. Center( // Use Center to vertically and horizontally center the form
  57. child: Column(
  58. mainAxisSize: MainAxisSize.min, // Minimize the column size to its content
  59. children: [
  60. if (!isRecording || showStartRecordingButton)
  61. Text(
  62. 'CookingLab',
  63. style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
  64. ),
  65. if (!isRecording || showStartRecordingButton)
  66. Form(
  67. key: _formKey,
  68. child: Container(
  69. width: double.maxFinite,
  70. padding: EdgeInsets.symmetric(horizontal: 23),
  71. child: Column(
  72. crossAxisAlignment: CrossAxisAlignment.start,
  73. children: [
  74. GestureDetector(
  75. onTap: () async {
  76. await showDialog(
  77. context: context,
  78. builder: (BuildContext context) {
  79. return Dialog(
  80. child: Container(
  81. height: MediaQuery.of(context).copyWith().size.height / 3,
  82. child: CupertinoTimerPicker(
  83. mode: CupertinoTimerPickerMode.hms,
  84. onTimerDurationChanged: (Duration duration) {
  85. _timeController1.text = "${duration.inHours.toString().padLeft(2, '0')}:${duration.inMinutes.remainder(60).toString().padLeft(2, '0')}:${duration.inSeconds.remainder(60).toString().padLeft(2, '0')}";
  86. },
  87. initialTimerDuration: Duration(hours: DateTime.now().hour, minutes: DateTime.now().minute, seconds: DateTime.now().second),
  88. ),
  89. ),
  90. );
  91. },
  92. );
  93. },
  94. child: AbsorbPointer(
  95. child: CustomTextFormField(
  96. controller: _timeController1,
  97. prefixIcon: Icon(Icons.timer),
  98. hintText: "Inserisci la durata",
  99. decoration: InputDecoration(
  100. border: OutlineInputBorder(),
  101. ),
  102. margin: EdgeInsets.fromLTRB(5, 12, 10, 0),
  103. validator: (value) {
  104. if (value != null && !isTime(value)) {
  105. return "Inserisci un'ora valida (HH:MM:SS)";
  106. }
  107. return null;
  108. },
  109. ),
  110. ),
  111. ),
  112. CustomTextFormField(
  113. controller: _titleController,
  114. prefixIcon: Icon(Icons.title),
  115. hintText: "Inserisci il titolo",
  116. margin: EdgeInsets.fromLTRB(5, 12, 10, 0),
  117. validator: (value) {
  118. if (value != null && !isText(value)) {
  119. return "Inserisci un testo valido"'';
  120. }
  121. return null;
  122. },
  123. ),
  124. CustomTextFormField(
  125. controller: _presenterNameController,
  126. prefixIcon: Icon(Icons.person),
  127. hintText: "Inserisci il nome del relatore",
  128. margin: EdgeInsets.fromLTRB(5, 12, 10, 0),
  129. validator: (value) {
  130. if (value != null && !isText(value)) {
  131. return "Inserisci un testo valido";
  132. }
  133. return null;
  134. },
  135. ),
  136. ],
  137. ),
  138. ),
  139. ),
  140. if (!isRecording || showStartRecordingButton)
  141. CustomButton(
  142. text: "Inizia a Registrare",
  143. margin: EdgeInsets.fromLTRB(50, 30, 50, 10),
  144. variant: ButtonVariant.OutlineBlack9003f,
  145. padding: ButtonPadding.PaddingAll9,
  146. onTap: () async {
  147. if (_formKey.currentState!.validate()) {
  148. setState(() {
  149. isLoading = true;
  150. });
  151. // Remove focus from any focused text field
  152. FocusScope.of(context).unfocus();
  153. String token = await loginAndGetToken();
  154. String time = _timeController1.text;
  155. String camNumber = _camNumberController.text;
  156. Map<String, dynamic> data = {
  157. "title": _titleController.text,
  158. "presenter_name": _presenterNameController.text,
  159. "language": _languageController.text,
  160. };
  161. try {
  162. await sendPostRequestStart(token, data, time, camNumber);
  163. setState(() {
  164. isRecording = true;
  165. showStartRecordingButton = false;
  166. });
  167. // Start the timer to update the UI after the duration ends
  168. List<String> timeParts = time.split(':');
  169. int durationInSeconds = int.parse(timeParts[0]) * 3600 + int.parse(timeParts[1]) * 60 + int.parse(timeParts[2]);
  170. _recordingTimer = Timer(Duration(seconds: durationInSeconds), () {
  171. setState(() {
  172. showStartRecordingButton = true;
  173. _resetFormFields();
  174. });
  175. });
  176. } catch (error) {
  177. setState(() {
  178. isError = true;
  179. });
  180. } finally {
  181. setState(() {
  182. isLoading = false;
  183. });
  184. }
  185. } else {
  186. Get.snackbar(
  187. "Errore",
  188. "Si prega di compilare tutti i campi richiesti",
  189. backgroundColor: Colors.red,
  190. colorText: Colors.white,
  191. );
  192. }
  193. },
  194. ),
  195. if (isRecording && !showStartRecordingButton && !isError)
  196. Expanded(
  197. child: Center(
  198. child: Column(
  199. mainAxisSize: MainAxisSize.min, // Minimize the column size to its content
  200. children: [
  201. Container(
  202. padding: EdgeInsets.all(40), // Increase the padding to increase the size of the Container
  203. decoration: BoxDecoration(
  204. color: Colors.white, // Change this to your preferred color
  205. borderRadius: BorderRadius.circular(15),
  206. boxShadow: [
  207. BoxShadow(
  208. color: Colors.grey.withOpacity(0.5),
  209. spreadRadius: 5,
  210. blurRadius: 7,
  211. offset: Offset(0, 3), // changes position of shadow
  212. ),
  213. ],
  214. ),
  215. child: Column(
  216. children: [
  217. Text(
  218. 'Dettagli di registrazione',
  219. style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.black),
  220. ),
  221. SizedBox(height: 10),
  222. Text(
  223. 'Durata: ${_timeController1.text}',
  224. style: TextStyle(fontSize: 20, color: Colors.black),
  225. ),
  226. SizedBox(height: 10),
  227. Text(
  228. 'Titolo: ${_titleController.text}',
  229. style: TextStyle(fontSize: 20, color: Colors.black),
  230. ),
  231. SizedBox(height: 10),
  232. Text(
  233. 'Nome del relatore: ${_presenterNameController.text}',
  234. style: TextStyle(fontSize: 20, color: Colors.black),
  235. ),
  236. ],
  237. ),
  238. ),
  239. CustomButton(
  240. text: "Interrompi la registrazione",
  241. margin: EdgeInsets.fromLTRB(80, 10, 80, 10),
  242. variant: ButtonVariant.OutlineBlack9003f,
  243. padding: ButtonPadding.PaddingAll15,
  244. onTap: () async {
  245. String token = await loginAndGetToken();
  246. String camNumber = _camNumberController.text;
  247. await sendPostRequestStop(token, camNumber);
  248. setState(() {
  249. isRecording = false;
  250. showStartRecordingButton = true;
  251. _resetFormFields();
  252. });
  253. },
  254. ),
  255. ],
  256. ),
  257. ),
  258. ),
  259. ],
  260. ),
  261. ),
  262. if (isLoading)
  263. Positioned.fill(
  264. child: Container(
  265. color: Colors.black54,
  266. child: Center(
  267. child: CircularProgressIndicator(),
  268. ),
  269. ),
  270. ),
  271. Positioned(
  272. bottom: 20,
  273. left: 0,
  274. right: 0,
  275. child: isLoading
  276. ? SizedBox() // Hide the button when isLoading is true
  277. : CustomButton(
  278. text: "Vai a Fotocamera",
  279. // prefixIcon: Icon(Icons.camera_alt),
  280. margin: EdgeInsets.symmetric(horizontal: 50, vertical: 30),
  281. variant: ButtonVariant.OutlineBlack9003f,
  282. padding: ButtonPadding.PaddingAll9,
  283. onTap: () {
  284. Navigator.push(
  285. context,
  286. MaterialPageRoute(builder: (context) => CameraPage()),
  287. );
  288. },
  289. ),
  290. ),
  291. ],
  292. ),
  293. ),
  294. ));
  295. }
  296. void _resetFormFields() {
  297. _timeController1.clear();
  298. _titleController.clear();
  299. _presenterNameController.clear();
  300. }
  301. Future<String> loginAndGetToken() async {
  302. var url = Uri.parse('https://192.168.60.230:5050/auth/login');
  303. var data = {'username': 'admin', 'password': 'Pwdadmin1!'};
  304. var body = data.keys.map((key) => "${Uri.encodeComponent(key)}=${Uri.encodeComponent(data[key] ?? '')}").join("&");
  305. HttpClient httpClient = new HttpClient()
  306. ..badCertificateCallback =
  307. ((X509Certificate cert, String host, int port) => true);
  308. var request = await httpClient.postUrl(url)
  309. ..headers.contentType = ContentType("application", "x-www-form-urlencoded")
  310. ..write(body);
  311. var response = await request.close();
  312. var responseBody = await utf8.decodeStream(response);
  313. var parsedJson = jsonDecode(responseBody);
  314. if (parsedJson['access_token'] != null) {
  315. return parsedJson['access_token'];
  316. } else {
  317. throw Exception('Token not found in response');
  318. }
  319. }
  320. Future<void> sendPostRequestStart(String token, Map<String, dynamic> data, String time, String camNumber) async {
  321. var url = Uri.parse('https://192.168.60.230:5050/start-recording');
  322. List<String> timeParts = time.split(':');
  323. int durationInSeconds = int.parse(timeParts[0]) * 3600 + int.parse(timeParts[1]) * 60 + int.parse(timeParts[2]);
  324. durationInSeconds += 10;
  325. var requestData = {
  326. "camera_name": int.parse(camNumber),
  327. "duration": durationInSeconds,
  328. "title": data['title'],
  329. "presenter_name": data['presenter_name'],
  330. "language": data['language']
  331. };
  332. var jsonData = jsonEncode(requestData);
  333. HttpClient httpClient = new HttpClient()
  334. ..badCertificateCallback =
  335. ((X509Certificate cert, String host, int port) => true);
  336. var request = await httpClient.postUrl(url)
  337. ..headers.contentType = ContentType.json
  338. ..headers.add('Authorization', 'Bearer $token')
  339. ..write(jsonData);
  340. var response = await request.close();
  341. var responseBody = await utf8.decodeStream(response);
  342. var responseJson = jsonDecode(responseBody);
  343. var message = responseJson['message'];
  344. bool isError = message.startsWith('Error:') || message.startsWith('Errore:');
  345. Get.snackbar(
  346. isError ? "Error" : "Message",
  347. message,
  348. backgroundColor: isError ? Colors.red : Colors.green,
  349. colorText: Colors.white,
  350. );
  351. setState(() {
  352. this.isError = isError;
  353. if (isError) {
  354. isRecording = false;
  355. throw Exception('Failed to start recording');
  356. }
  357. });
  358. }
  359. Future<void> sendPostRequestStop(String token, String camNumber) async {
  360. var url = Uri.parse('https://192.168.60.230:5050/stop-recording');
  361. var requestData = {
  362. "camera_name": int.parse(camNumber),
  363. };
  364. var jsonData = jsonEncode(requestData);
  365. HttpClient httpClient = new HttpClient()
  366. ..badCertificateCallback =
  367. ((X509Certificate cert, String host, int port) => true);
  368. var request = await httpClient.postUrl(url)
  369. ..headers.contentType = ContentType.json
  370. ..headers.add('Authorization', 'Bearer $token')
  371. ..write(jsonData);
  372. var response = await request.close();
  373. var responseBody = await utf8.decodeStream(response);
  374. var responseJson = jsonDecode(responseBody);
  375. var message = responseJson['message'];
  376. bool isError = message.startsWith('Error:') || message.startsWith('Errore:');
  377. Get.snackbar(
  378. isError ? "Error" : "Message",
  379. message,
  380. backgroundColor: isError ? Colors.red : Colors.green,
  381. colorText: Colors.white,
  382. );
  383. setState(() {
  384. this.isError = isError;
  385. if (!isError) {
  386. isRecording = false;
  387. _timeController1.clear();
  388. _titleController.clear();
  389. _presenterNameController.clear();
  390. }
  391. });
  392. }
  393. bool isTime(String time) {
  394. var timeParts = time.split(':');
  395. if (timeParts.length != 3) {
  396. return false;
  397. }
  398. var hours = int.tryParse(timeParts[0]);
  399. var minutes = int.tryParse(timeParts[1]);
  400. var seconds = int.tryParse(timeParts[2]);
  401. return hours != null && hours >= 0 && hours < 24 &&
  402. minutes != null && minutes >= 0 && minutes < 60 &&
  403. seconds != null && seconds >= 0 && seconds < 60;
  404. }
  405. bool isText(String? value) {
  406. return value != null && value.isNotEmpty;
  407. }
  408. }