From d7184f09aa86590a4af21535e2c58ecfff56c4dd Mon Sep 17 00:00:00 2001 From: lukas Date: Fri, 15 Apr 2022 09:16:41 +0200 Subject: [PATCH] add a video page and disply tiles correctly --- lib/api/api.dart | 1 - lib/api/token.dart | 3 + lib/login/login_screen.dart | 18 ++-- lib/main.dart | 29 +++++- lib/preview_tile.dart | 88 ++++++++++++++---- lib/video_feed.dart | 69 +++++++++----- pubspec.lock | 177 ++++++++++++++++++++++++++++++++++-- pubspec.yaml | 3 + 8 files changed, 327 insertions(+), 61 deletions(-) diff --git a/lib/api/api.dart b/lib/api/api.dart index a4d5883..360d054 100644 --- a/lib/api/api.dart +++ b/lib/api/api.dart @@ -10,7 +10,6 @@ class API { String apinode, String action, Object payload) async { final Completer 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'), diff --git a/lib/api/token.dart b/lib/api/token.dart index 775b0aa..ca314f7 100644 --- a/lib/api/token.dart +++ b/lib/api/token.dart @@ -31,8 +31,11 @@ class Token { WidgetsFlutterBinding.ensureInitialized(); 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 && domain != null) { + _tokenval = token; + _domain = domain; completer.complete(TokenT(token, domain)); } else { Log.d("no token defined"); diff --git a/lib/login/login_screen.dart b/lib/login/login_screen.dart index 10b28b1..b1ba987 100644 --- a/lib/login/login_screen.dart +++ b/lib/login/login_screen.dart @@ -60,8 +60,7 @@ class _LoginScreenState extends State { Widget build(BuildContext context) { return Container( decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage('assets/images/login.png'), fit: BoxFit.cover), + image: DecorationImage(image: AssetImage('assets/images/login.png'), fit: BoxFit.cover), ), child: Scaffold( backgroundColor: Colors.transparent, @@ -76,8 +75,7 @@ class _LoginScreenState extends State { ), SingleChildScrollView( child: Container( - padding: EdgeInsets.only( - top: MediaQuery.of(context).size.height * 0.5), + padding: EdgeInsets.only(top: MediaQuery.of(context).size.height * 0.5), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -88,6 +86,7 @@ class _LoginScreenState extends State { TextField( controller: _domainTextController, style: const TextStyle(color: Colors.black), + autofocus: true, decoration: InputDecoration( fillColor: Colors.grey.shade100, filled: true, @@ -103,6 +102,7 @@ class _LoginScreenState extends State { controller: _passwordTextController, style: const TextStyle(), obscureText: true, + autofocus: true, decoration: InputDecoration( fillColor: Colors.grey.shade100, filled: true, @@ -119,8 +119,7 @@ class _LoginScreenState extends State { children: [ const Text( 'Sign in', - style: TextStyle( - fontSize: 27, fontWeight: FontWeight.w700), + style: TextStyle(fontSize: 27, fontWeight: FontWeight.w700), ), CircleAvatar( radius: 30, @@ -128,10 +127,9 @@ class _LoginScreenState extends State { child: IconButton( color: Colors.white, onPressed: () { - final pwd = - _passwordTextController.value.text; - final domain = - _domainTextController.value.text; + Log.d("clickkked"); + final pwd = _passwordTextController.value.text; + final domain = _domainTextController.value.text; login(pwd, domain).then((value) { if (value != "") { setState(() { diff --git a/lib/main.dart b/lib/main.dart index ca8d9df..e72543f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,11 +1,36 @@ +import "package:dart_vlc/dart_vlc.dart"; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:openmediacentermobile/app.dart'; import 'log/log.dart'; import 'login/logincontext.dart'; +import 'platform.dart'; // main app entry point -void main() { +void main() async { Log.i("App init!"); - runApp(const LoginContainer(child: App())); + if (isDesktop()) { + DartVLC.initialize(); + } else { + await loadDeviceInfo(); + } + + // RawKeyboard.instance.addListener((event) { + // if (LogicalKeyboardKey.arrowLeft == event.logicalKey) { + // FocusManager.instance.primaryFocus?.focusInDirection(TraversalDirection.left); + // } else if (LogicalKeyboardKey.arrowRight == event.logicalKey) { + // FocusManager.instance.primaryFocus?.focusInDirection(TraversalDirection.right); + // } else if (LogicalKeyboardKey.arrowDown == event.logicalKey) { + // FocusManager.instance.primaryFocus?.focusInDirection(TraversalDirection.down); + // } else if (LogicalKeyboardKey.arrowUp == event.logicalKey) { + // FocusManager.instance.primaryFocus?.focusInDirection(TraversalDirection.up); + // } + // }); + + runApp(Shortcuts(shortcuts: { + LogicalKeySet(LogicalKeyboardKey.select): ActivateIntent(), + }, child: const LoginContainer(child: App()))); + + // runApp(const LoginContainer(child: App())); } diff --git a/lib/preview_tile.dart b/lib/preview_tile.dart index 3ea87cb..63a7079 100644 --- a/lib/preview_tile.dart +++ b/lib/preview_tile.dart @@ -1,8 +1,10 @@ import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:openmediacentermobile/videoscreen.dart'; import 'api/api.dart'; +import 'platform.dart'; class VideoT { int id; @@ -12,8 +14,7 @@ class VideoT { 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()); + return VideoT(json['MovieName'] as String, json['MovieId'] as int, (json['Ratio'] as num).toDouble()); } } @@ -26,31 +27,82 @@ class PreviewTile extends StatefulWidget { } class _PreviewTileState extends State { - String prev = ""; + late Future _preview; @override void initState() { super.initState(); - API.query("video", "readThumbnail", {'Movieid': widget.dta.id}).then( - (value) { - setState(() { - prev = value.substring(23); - }); - }); + _preview = loadData(); + } + + Future loadData() async { + final data = await API.query("video", "readThumbnail", {'Movieid': widget.dta.id}); + + final img = Image.memory( + base64Decode(data.substring(23)), + width: double.infinity, + fit: BoxFit.fitWidth, + ); + + // precache image to avoid loading time to render image + await precacheImage(img.image, context); + + return img; } @override Widget build(BuildContext context) { - return InkWell( - child: Column( - children: [ - Text(widget.dta.title), - prev != "" - ? Image.memory(base64Decode(prev)) - : const CircularProgressIndicator() - ], - ), + return FutureBuilder( + future: _preview, // a previously-obtained Future or null + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasError) { + return Text("Error"); + } else if (snapshot.hasData) { + return ClipRRect( + borderRadius: BorderRadius.circular(20.0), + child: Stack( + children: [ + Container( + child: Column( + children: [Text(widget.dta.title, style: TextStyle(fontSize: isTV() ? 8 : 12)), snapshot.data!], + ), + color: Colors.green, + ), + Positioned.fill( + child: Material( + color: Colors.transparent, + child: InkWell( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => VideoScreen(videoID: widget.dta.id), + ), + ); + }, + ), + ), + ), + ], + ), + ); + } else { + return Column(children: const [ + SizedBox(height: 100), + SizedBox( + width: 60, + height: 60, + child: CircularProgressIndicator(), + ), + Padding( + padding: EdgeInsets.only(top: 16), + child: Text('Awaiting result...'), + ), + SizedBox(height: 100), + ]); + } + }, ); } } diff --git a/lib/video_feed.dart b/lib/video_feed.dart index 82c988a..43f9801 100644 --- a/lib/video_feed.dart +++ b/lib/video_feed.dart @@ -4,7 +4,9 @@ import 'package:flutter/material.dart'; import 'package:openmediacentermobile/api/api.dart'; import 'log/log.dart'; +import 'platform.dart'; import 'preview_tile.dart'; +import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; class VideoFeed extends StatefulWidget { const VideoFeed({Key? key}) : super(key: key); @@ -16,36 +18,59 @@ class VideoFeed extends StatefulWidget { } class VideoFeedState extends State { - List _vids = []; + late Future> _data; + + Future> loadData() async { + final data = await API.query("video", "getMovies", {'Tag': 1, 'Sort': 0}); + + final d = jsonDecode(data); + + List dta = (d['Videos'] as List).map((e) => VideoT.fromJson(e)).toList(); + + return dta; + } @override void initState() { super.initState(); - API.query("video", "getMovies", {'Tag': 1, 'Sort': 0}).then((value) { - final d = jsonDecode(value); - - List dta = - (d['Videos'] as List).map((e) => VideoT.fromJson(e)).toList(); - - setState(() { - _vids = dta; - }); - }); + _data = loadData(); } @override Widget build(BuildContext context) { - return GridView.builder( - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - childAspectRatio: MediaQuery.of(context).size.width / - MediaQuery.of(context).size.height, - crossAxisSpacing: 10), - itemCount: _vids.length, - itemBuilder: (context, index) { - Log.d("item $index built!"); - return PreviewTile(dta: _vids[index]); - }); + double width = MediaQuery.of(context).size.width; + Log.d(width); + + return FutureBuilder>( + future: _data, // a previously-obtained Future or null + builder: (BuildContext context, AsyncSnapshot> snapshot) { + if (snapshot.hasError) { + return Text("Error"); + } else if (snapshot.hasData) { + return MasonryGridView.count( + // every tile should be at max 330 pixels long... + crossAxisCount: isTV() ? width ~/ 200 : width ~/ 330, + mainAxisSpacing: 4, + crossAxisSpacing: 4, + itemBuilder: (context, index) { + return PreviewTile(dta: snapshot.data![index]); + }, + ); + } else { + return Column(children: const [ + SizedBox( + width: 60, + height: 60, + child: CircularProgressIndicator(), + ), + Padding( + padding: EdgeInsets.only(top: 16), + child: Text('Awaiting result...'), + ) + ]); + } + }, + ); } } diff --git a/pubspec.lock b/pubspec.lock index 43afe60..825950f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -8,6 +8,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.8.2" + audio_video_progress_bar: + dependency: transitive + description: + name: audio_video_progress_bar + url: "https://pub.dartlang.org" + source: hosted + version: "0.10.0" boolean_selector: dependency: transitive description: @@ -50,6 +57,62 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.4" + dart_vlc: + dependency: "direct main" + description: + name: dart_vlc + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.9" + dart_vlc_ffi: + dependency: transitive + description: + name: dart_vlc_ffi + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5+1" + device_info_plus: + dependency: "direct main" + description: + name: device_info_plus + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.3" + device_info_plus_linux: + dependency: transitive + description: + name: device_info_plus_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + device_info_plus_macos: + dependency: transitive + description: + name: device_info_plus_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.3" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.0+1" + device_info_plus_web: + dependency: transitive + description: + name: device_info_plus_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + device_info_plus_windows: + dependency: transitive + description: + name: device_info_plus_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" fake_async: dependency: transitive description: @@ -57,6 +120,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.2" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.2" flutter: dependency: "direct main" description: flutter @@ -111,6 +188,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.2" + flutter_staggered_grid_view: + dependency: "direct main" + description: + name: flutter_staggered_grid_view + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.1" flutter_test: dependency: "direct dev" description: flutter @@ -141,7 +225,7 @@ packages: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.3" + version: "0.6.4" lints: dependency: transitive description: @@ -169,7 +253,7 @@ packages: name: material_color_utilities url: "https://pub.dartlang.org" source: hosted - version: "0.1.2" + version: "0.1.4" meta: dependency: transitive description: @@ -183,7 +267,63 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" + path_provider: + dependency: transitive + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.9" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.12" + path_provider_ios: + dependency: transitive + description: + name: path_provider_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.8" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.5" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" + platform: + dependency: transitive + description: + name: platform + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" plugin_platform_interface: dependency: transitive description: @@ -191,6 +331,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.2" + process: + dependency: transitive + description: + name: process + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.4" sky_engine: dependency: transitive description: flutter @@ -202,7 +349,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.8.2" stack_trace: dependency: transitive description: @@ -237,7 +384,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.8" + version: "0.4.9" typed_data: dependency: transitive description: @@ -251,7 +398,21 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.1.2" + win32: + dependency: transitive + description: + name: win32 + url: "https://pub.dartlang.org" + source: hosted + version: "2.5.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0+1" sdks: - dart: ">=2.15.0 <3.0.0" - flutter: ">=2.0.0" + dart: ">=2.16.0-100.0.dev <3.0.0" + flutter: ">=2.8.0" diff --git a/pubspec.yaml b/pubspec.yaml index 8898cbc..2506c9f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,6 +37,9 @@ dependencies: flutter_secure_storage: ^5.0.2 logger: ^1.1.0 http: ^0.13.4 + flutter_staggered_grid_view: ^0.6.1 + dart_vlc: ^0.1.9 + device_info_plus: ^3.2.3 dev_dependencies: flutter_test: