add loginpage, load videos, load preview
This commit is contained in:
		| @@ -1,5 +1,6 @@ | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     package="eu.heili.openmediacentermobile"> | ||||
|     <uses-permission android:name="android.permission.INTERNET"/> | ||||
|    <application | ||||
|         android:label="openmediacentermobile" | ||||
|         android:name="${applicationName}" | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								assets/images/logo_circle.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/images/logo_circle.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 25 KiB | 
							
								
								
									
										31
									
								
								lib/api/api.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								lib/api/api.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| import 'dart:async'; | ||||
| import 'dart:convert'; | ||||
|  | ||||
| import 'package:http/http.dart' as http; | ||||
| import 'package:openmediacentermobile/api/token.dart'; | ||||
| import 'package:openmediacentermobile/log/log.dart'; | ||||
|  | ||||
| class API { | ||||
|   static Future<String> query( | ||||
|       String apinode, String action, Object payload) async { | ||||
|     final Completer<String> cmpl = Completer(); | ||||
|     final t = await Token.getInstance().getToken(); | ||||
|     Log.d(t); | ||||
|     if (t != null) { | ||||
|       final resp = await http.post( | ||||
|         Uri.parse(t.domain + '/api/$apinode/$action'), | ||||
|         headers: <String, String>{ | ||||
|           'Content-Type': 'application/json; charset=UTF-8', | ||||
|           'Token': t.token | ||||
|         }, | ||||
|         body: jsonEncode(payload), | ||||
|       ); | ||||
|  | ||||
|       cmpl.complete(resp.body); | ||||
|     } else { | ||||
|       cmpl.complete(""); | ||||
|     } | ||||
|  | ||||
|     return cmpl.future; | ||||
|   } | ||||
| } | ||||
| @@ -5,43 +5,50 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart'; | ||||
|  | ||||
| import '../log/log.dart'; | ||||
|  | ||||
| class TokenT { | ||||
|   String token; | ||||
|   String domain; | ||||
|  | ||||
|   TokenT(this.token, this.domain); | ||||
| } | ||||
|  | ||||
| class Token { | ||||
|   static final Token _token = Token._(); | ||||
|   final _storage = const FlutterSecureStorage(); | ||||
|  | ||||
|   String _tokenval = ""; | ||||
|   String _domain = ""; | ||||
|  | ||||
|   static Token getInstance() { | ||||
|     return _token; | ||||
|   } | ||||
|  | ||||
|   Future<String?> _readToken() async { | ||||
|   Future<TokenT?> getToken() async { | ||||
|     var completer = Completer<TokenT?>(); | ||||
|  | ||||
|     if (_tokenval == "" || _domain == "") { | ||||
|       Log.d("reading token store"); | ||||
|       WidgetsFlutterBinding.ensureInitialized(); | ||||
|     return _storage.read(key: 'jwt'); | ||||
|   } | ||||
|  | ||||
|   Future<String> getToken() async { | ||||
|     var completer = Completer<String>(); | ||||
|  | ||||
|     if (_tokenval == "") { | ||||
|       final token = await _readToken(); | ||||
|       final token = await _storage.read(key: 'jwt'); | ||||
|       final domain = await _storage.read(key: 'domain'); | ||||
|       // check if value is defined in phone store | ||||
|       if (token != null) { | ||||
|         completer.complete(token); | ||||
|       if (token != null && domain != null) { | ||||
|         completer.complete(TokenT(token, domain)); | ||||
|       } else { | ||||
|         Log.d("no token defined"); | ||||
|         completer.complete(""); | ||||
|         completer.complete(null); | ||||
|       } | ||||
|     } else { | ||||
|       completer.complete(_tokenval); | ||||
|       completer.complete(TokenT(_tokenval, _domain)); | ||||
|     } | ||||
|     return completer.future; | ||||
|   } | ||||
|  | ||||
|   void setToken(String token) { | ||||
|   void setToken(String token, String domain) { | ||||
|     _tokenval = token; | ||||
|     _domain = domain; | ||||
|     _storage.write(key: 'jwt', value: token); | ||||
|     _storage.write(key: 'domain', value: domain); | ||||
|   } | ||||
|  | ||||
|   Token._(); | ||||
|   | ||||
							
								
								
									
										13
									
								
								lib/app.dart
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								lib/app.dart
									
									
									
									
									
								
							| @@ -1,7 +1,8 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:logger/logger.dart'; | ||||
| import 'package:openmediacentermobile/log/log.dart'; | ||||
| import 'package:openmediacentermobile/login/login_screen.dart'; | ||||
|  | ||||
| import 'api/token.dart'; | ||||
| import 'login/logincontext.dart'; | ||||
| import 'video_feed.dart'; | ||||
|  | ||||
| @@ -18,15 +19,10 @@ class App extends StatelessWidget { | ||||
|   Widget build(BuildContext context) { | ||||
|     var loginctx = LoginContext.of(context); | ||||
|  | ||||
|     Logger().d("We are logged in: ${loginctx.LoggedIn}"); | ||||
|     Log.d("We are logged in: ${loginctx.LoggedIn}"); | ||||
|  | ||||
|     if (!loginctx.LoggedIn) { | ||||
|       return MaterialApp( | ||||
|           home: Scaffold( | ||||
|               appBar: AppBar( | ||||
|                 title: const Text("Login"), | ||||
|               ), | ||||
|               body: LoginScreen())); | ||||
|       return const MaterialApp(home: LoginScreen()); | ||||
|     } else { | ||||
|       return MaterialApp( | ||||
|           home: Scaffold( | ||||
| @@ -37,6 +33,7 @@ class App extends StatelessWidget { | ||||
|                     ElevatedButton( | ||||
|                         onPressed: () { | ||||
|                           loginctx.onLoggin(false); | ||||
|                           Token.getInstance().setToken("", ""); | ||||
|                         }, | ||||
|                         child: Text("logout")) | ||||
|                   ], | ||||
|   | ||||
| @@ -1,23 +1,162 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'dart:async'; | ||||
| import 'dart:convert'; | ||||
|  | ||||
| import 'logincontext.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:http/http.dart' as http; | ||||
| import 'package:openmediacentermobile/api/token.dart'; | ||||
| import 'package:openmediacentermobile/log/log.dart'; | ||||
| import 'package:openmediacentermobile/login/logincontext.dart'; | ||||
|  | ||||
| class LoginScreen extends StatefulWidget { | ||||
|   const LoginScreen({Key? key, this.onLogin}) : super(key: key); | ||||
|  | ||||
|   final onLogin; | ||||
|   const LoginScreen({Key? key}) : super(key: key); | ||||
|  | ||||
|   @override | ||||
|   _LoginScreenState createState() => _LoginScreenState(); | ||||
| } | ||||
|  | ||||
| class _LoginScreenState extends State<LoginScreen> { | ||||
|   final TextEditingController _domainTextController = TextEditingController(); | ||||
|   final TextEditingController _passwordTextController = TextEditingController(); | ||||
|   String error = ""; | ||||
|  | ||||
|   Future<String> login(String password, String domain) async { | ||||
|     Log.i("logging in..."); | ||||
|     final compl = Completer<String>(); | ||||
|     final resp = await http.post( | ||||
|       Uri.parse(domain + '/api/login/login'), | ||||
|       headers: <String, String>{ | ||||
|         'Content-Type': 'application/json; charset=UTF-8', | ||||
|       }, | ||||
|       body: jsonEncode(<String, String>{ | ||||
|         'Password': password, | ||||
|       }), | ||||
|     ); | ||||
|  | ||||
|     if (resp.statusCode != 200) { | ||||
|       compl.complete(resp.body); | ||||
|     } else { | ||||
|       final json = jsonDecode(resp.body); | ||||
|       final token = json["Token"]; | ||||
|  | ||||
|       Token.getInstance().setToken(token, domain); | ||||
|       LoginContext.of(context).onLoggin(true); | ||||
|  | ||||
|       compl.complete(""); | ||||
|     } | ||||
|  | ||||
|     // LoginContext.of(context).onLoggin(true); | ||||
|  | ||||
|     return compl.future; | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void dispose() { | ||||
|     _domainTextController.dispose(); | ||||
|     _passwordTextController.dispose(); | ||||
|     super.dispose(); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return ElevatedButton( | ||||
|     return Container( | ||||
|       decoration: const BoxDecoration( | ||||
|         image: DecorationImage( | ||||
|             image: AssetImage('assets/images/login.png'), fit: BoxFit.cover), | ||||
|       ), | ||||
|       child: Scaffold( | ||||
|         backgroundColor: Colors.transparent, | ||||
|         body: Stack( | ||||
|           children: [ | ||||
|             Container( | ||||
|               padding: const EdgeInsets.only(left: 35, top: 90), | ||||
|               child: const Text( | ||||
|                 'Welcome\nBack', | ||||
|                 style: TextStyle(color: Colors.white, fontSize: 33), | ||||
|               ), | ||||
|             ), | ||||
|             SingleChildScrollView( | ||||
|               child: Container( | ||||
|                 padding: EdgeInsets.only( | ||||
|                     top: MediaQuery.of(context).size.height * 0.5), | ||||
|                 child: Column( | ||||
|                   crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                   children: [ | ||||
|                     Container( | ||||
|                       margin: const EdgeInsets.only(left: 35, right: 35), | ||||
|                       child: Column( | ||||
|                         children: [ | ||||
|                           TextField( | ||||
|                             controller: _domainTextController, | ||||
|                             style: const TextStyle(color: Colors.black), | ||||
|                             decoration: InputDecoration( | ||||
|                                 fillColor: Colors.grey.shade100, | ||||
|                                 filled: true, | ||||
|                                 hintText: "Domain", | ||||
|                                 border: OutlineInputBorder( | ||||
|                                   borderRadius: BorderRadius.circular(10), | ||||
|                                 )), | ||||
|                           ), | ||||
|                           const SizedBox( | ||||
|                             height: 30, | ||||
|                           ), | ||||
|                           TextField( | ||||
|                             controller: _passwordTextController, | ||||
|                             style: const TextStyle(), | ||||
|                             obscureText: true, | ||||
|                             decoration: InputDecoration( | ||||
|                                 fillColor: Colors.grey.shade100, | ||||
|                                 filled: true, | ||||
|                                 hintText: "Password", | ||||
|                                 border: OutlineInputBorder( | ||||
|                                   borderRadius: BorderRadius.circular(10), | ||||
|                                 )), | ||||
|                           ), | ||||
|                           const SizedBox( | ||||
|                             height: 40, | ||||
|                           ), | ||||
|                           Row( | ||||
|                             mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                             children: [ | ||||
|                               const Text( | ||||
|                                 'Sign in', | ||||
|                                 style: TextStyle( | ||||
|                                     fontSize: 27, fontWeight: FontWeight.w700), | ||||
|                               ), | ||||
|                               CircleAvatar( | ||||
|                                 radius: 30, | ||||
|                                 backgroundColor: const Color(0xff4c505b), | ||||
|                                 child: IconButton( | ||||
|                                     color: Colors.white, | ||||
|                                     onPressed: () { | ||||
|           LoginContext.of(context).onLoggin(true); | ||||
|                                       final pwd = | ||||
|                                           _passwordTextController.value.text; | ||||
|                                       final domain = | ||||
|                                           _domainTextController.value.text; | ||||
|                                       login(pwd, domain).then((value) { | ||||
|                                         if (value != "") { | ||||
|                                           setState(() { | ||||
|                                             error = value; | ||||
|                                           }); | ||||
|                                         } | ||||
|                                       }); | ||||
|                                     }, | ||||
|         child: Text("klick meee")); | ||||
|                                     icon: const Icon( | ||||
|                                       Icons.arrow_forward, | ||||
|                                     )), | ||||
|                               ) | ||||
|                             ], | ||||
|                           ), | ||||
|                           error != "" ? Text(error) : const Text("") | ||||
|                         ], | ||||
|                       ), | ||||
|                     ) | ||||
|                   ], | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -17,14 +17,12 @@ class _LoginContainerState extends State<LoginContainer> { | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     // TODO: implement initState | ||||
|     super.initState(); | ||||
|  | ||||
|     final token = Token.getInstance(); | ||||
|     token.getToken().then((value) { | ||||
|       // todo this context call might occur before app rendered correctly! | ||||
|       Log.i("The token value is $value"); | ||||
|       if (value != "") { | ||||
|       if (value != null) { | ||||
|         setState(() { | ||||
|           loggedIn = true; | ||||
|           loading = false; | ||||
|   | ||||
| @@ -2,24 +2,54 @@ import 'dart:convert'; | ||||
|  | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
| class VideoPreview { | ||||
|   String thumbnail; | ||||
|   String title; | ||||
|   int id; | ||||
| import 'api/api.dart'; | ||||
|  | ||||
|   VideoPreview(this.thumbnail, this.title, this.id); | ||||
| class VideoT { | ||||
|   int id; | ||||
|   String title; | ||||
|   double Ratio; | ||||
|  | ||||
|   VideoT(this.title, this.id, this.Ratio); | ||||
|  | ||||
|   factory VideoT.fromJson(dynamic json) { | ||||
|     return VideoT(json['MovieName'] as String, json['MovieId'] as int, | ||||
|         (json['Ratio'] as num).toDouble()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| class PreviewTile extends StatelessWidget { | ||||
| class PreviewTile extends StatefulWidget { | ||||
|   const PreviewTile({Key? key, required this.dta}) : super(key: key); | ||||
|   final VideoT dta; | ||||
|  | ||||
|   final VideoPreview dta; | ||||
|   @override | ||||
|   _PreviewTileState createState() => _PreviewTileState(); | ||||
| } | ||||
|  | ||||
| class _PreviewTileState extends State<PreviewTile> { | ||||
|   String prev = ""; | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|  | ||||
|     API.query("video", "readThumbnail", {'Movieid': widget.dta.id}).then( | ||||
|         (value) { | ||||
|       setState(() { | ||||
|         prev = value.substring(23); | ||||
|       }); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return InkWell( | ||||
|       child: Column( | ||||
|         children: [Text(dta.title), Image.memory(base64Decode(dta.thumbnail))], | ||||
|         children: [ | ||||
|           Text(widget.dta.title), | ||||
|           prev != "" | ||||
|               ? Image.memory(base64Decode(prev)) | ||||
|               : const CircularProgressIndicator() | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|   | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										14
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								pubspec.lock
									
									
									
									
									
								
							| @@ -121,6 +121,20 @@ packages: | ||||
|     description: flutter | ||||
|     source: sdk | ||||
|     version: "0.0.0" | ||||
|   http: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: http | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "0.13.4" | ||||
|   http_parser: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: http_parser | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "4.0.0" | ||||
|   js: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|   | ||||
| @@ -36,6 +36,7 @@ dependencies: | ||||
|   cupertino_icons: ^1.0.2 | ||||
|   flutter_secure_storage: ^5.0.2 | ||||
|   logger: ^1.1.0 | ||||
|   http: ^0.13.4 | ||||
|  | ||||
| dev_dependencies: | ||||
|   flutter_test: | ||||
| @@ -63,6 +64,9 @@ flutter: | ||||
|   # assets: | ||||
|   #   - images/a_dot_burr.jpeg | ||||
|   #   - images/a_dot_ham.jpeg | ||||
|   assets: | ||||
|     - assets/images/logo_circle.png | ||||
|     - assets/images/login.png | ||||
|  | ||||
|   # An image asset can refer to one or more resolution-specific "variants", see | ||||
|   # https://flutter.dev/assets-and-images/#resolution-aware. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user