add loginpage, load videos, load preview
This commit is contained in:
parent
d1f6f02fcc
commit
af5db6242e
@ -1,5 +1,6 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="eu.heili.openmediacentermobile">
|
package="eu.heili.openmediacentermobile">
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<application
|
<application
|
||||||
android:label="openmediacentermobile"
|
android:label="openmediacentermobile"
|
||||||
android:name="${applicationName}"
|
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';
|
import '../log/log.dart';
|
||||||
|
|
||||||
|
class TokenT {
|
||||||
|
String token;
|
||||||
|
String domain;
|
||||||
|
|
||||||
|
TokenT(this.token, this.domain);
|
||||||
|
}
|
||||||
|
|
||||||
class Token {
|
class Token {
|
||||||
static final Token _token = Token._();
|
static final Token _token = Token._();
|
||||||
final _storage = const FlutterSecureStorage();
|
final _storage = const FlutterSecureStorage();
|
||||||
|
|
||||||
String _tokenval = "";
|
String _tokenval = "";
|
||||||
|
String _domain = "";
|
||||||
|
|
||||||
static Token getInstance() {
|
static Token getInstance() {
|
||||||
return _token;
|
return _token;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> _readToken() async {
|
Future<TokenT?> getToken() async {
|
||||||
|
var completer = Completer<TokenT?>();
|
||||||
|
|
||||||
|
if (_tokenval == "" || _domain == "") {
|
||||||
Log.d("reading token store");
|
Log.d("reading token store");
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
return _storage.read(key: 'jwt');
|
final token = await _storage.read(key: 'jwt');
|
||||||
}
|
final domain = await _storage.read(key: 'domain');
|
||||||
|
|
||||||
Future<String> getToken() async {
|
|
||||||
var completer = Completer<String>();
|
|
||||||
|
|
||||||
if (_tokenval == "") {
|
|
||||||
final token = await _readToken();
|
|
||||||
// check if value is defined in phone store
|
// check if value is defined in phone store
|
||||||
if (token != null) {
|
if (token != null && domain != null) {
|
||||||
completer.complete(token);
|
completer.complete(TokenT(token, domain));
|
||||||
} else {
|
} else {
|
||||||
Log.d("no token defined");
|
Log.d("no token defined");
|
||||||
completer.complete("");
|
completer.complete(null);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
completer.complete(_tokenval);
|
completer.complete(TokenT(_tokenval, _domain));
|
||||||
}
|
}
|
||||||
return completer.future;
|
return completer.future;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setToken(String token) {
|
void setToken(String token, String domain) {
|
||||||
_tokenval = token;
|
_tokenval = token;
|
||||||
|
_domain = domain;
|
||||||
_storage.write(key: 'jwt', value: token);
|
_storage.write(key: 'jwt', value: token);
|
||||||
|
_storage.write(key: 'domain', value: domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
Token._();
|
Token._();
|
||||||
|
13
lib/app.dart
13
lib/app.dart
@ -1,7 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:logger/logger.dart';
|
import 'package:openmediacentermobile/log/log.dart';
|
||||||
import 'package:openmediacentermobile/login/login_screen.dart';
|
import 'package:openmediacentermobile/login/login_screen.dart';
|
||||||
|
|
||||||
|
import 'api/token.dart';
|
||||||
import 'login/logincontext.dart';
|
import 'login/logincontext.dart';
|
||||||
import 'video_feed.dart';
|
import 'video_feed.dart';
|
||||||
|
|
||||||
@ -18,15 +19,10 @@ class App extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var loginctx = LoginContext.of(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) {
|
if (!loginctx.LoggedIn) {
|
||||||
return MaterialApp(
|
return const MaterialApp(home: LoginScreen());
|
||||||
home: Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: const Text("Login"),
|
|
||||||
),
|
|
||||||
body: LoginScreen()));
|
|
||||||
} else {
|
} else {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
@ -37,6 +33,7 @@ class App extends StatelessWidget {
|
|||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
loginctx.onLoggin(false);
|
loginctx.onLoggin(false);
|
||||||
|
Token.getInstance().setToken("", "");
|
||||||
},
|
},
|
||||||
child: Text("logout"))
|
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 {
|
class LoginScreen extends StatefulWidget {
|
||||||
const LoginScreen({Key? key, this.onLogin}) : super(key: key);
|
const LoginScreen({Key? key}) : super(key: key);
|
||||||
|
|
||||||
final onLogin;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_LoginScreenState createState() => _LoginScreenState();
|
_LoginScreenState createState() => _LoginScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LoginScreenState extends State<LoginScreen> {
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
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: () {
|
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
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
// TODO: implement initState
|
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
final token = Token.getInstance();
|
final token = Token.getInstance();
|
||||||
token.getToken().then((value) {
|
token.getToken().then((value) {
|
||||||
// todo this context call might occur before app rendered correctly!
|
|
||||||
Log.i("The token value is $value");
|
Log.i("The token value is $value");
|
||||||
if (value != "") {
|
if (value != null) {
|
||||||
setState(() {
|
setState(() {
|
||||||
loggedIn = true;
|
loggedIn = true;
|
||||||
loading = false;
|
loading = false;
|
||||||
|
@ -2,24 +2,54 @@ import 'dart:convert';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class VideoPreview {
|
import 'api/api.dart';
|
||||||
String thumbnail;
|
|
||||||
String title;
|
|
||||||
int id;
|
|
||||||
|
|
||||||
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);
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return InkWell(
|
return InkWell(
|
||||||
child: Column(
|
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
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
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:
|
js:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -36,6 +36,7 @@ dependencies:
|
|||||||
cupertino_icons: ^1.0.2
|
cupertino_icons: ^1.0.2
|
||||||
flutter_secure_storage: ^5.0.2
|
flutter_secure_storage: ^5.0.2
|
||||||
logger: ^1.1.0
|
logger: ^1.1.0
|
||||||
|
http: ^0.13.4
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
@ -63,6 +64,9 @@ flutter:
|
|||||||
# assets:
|
# assets:
|
||||||
# - images/a_dot_burr.jpeg
|
# - images/a_dot_burr.jpeg
|
||||||
# - images/a_dot_ham.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
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
# https://flutter.dev/assets-and-images/#resolution-aware.
|
# https://flutter.dev/assets-and-images/#resolution-aware.
|
||||||
|
Loading…
Reference in New Issue
Block a user