diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4a2c4f4..9254a57 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,3 +13,13 @@ flutter_build_android: #Job name artifacts: paths: - build/app/outputs/apk/release/app-release.apk + +flutter_lint: + stage: build + script: + - flutter format . --output none --set-exit-if-changed + +flutter_analyze: + stage: build + script: + - flutter analyze ./lib diff --git a/android/app/build.gradle b/android/app/build.gradle index 1be493f..fbc5ba7 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 33 + compileSdkVersion flutter.compileSdkVersion compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -45,7 +45,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "eu.heili.openmediacentermobile" minSdkVersion 21 - targetSdkVersion 30 + targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName } diff --git a/android/build.gradle b/android/build.gradle index 9e8dea7..10ab8e5 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.0' + ext.kotlin_version = '1.4.32' repositories { google() mavenCentral() diff --git a/lib/DrawerPage.dart b/lib/DrawerPage.dart index 4ac0809..c4219c1 100644 --- a/lib/DrawerPage.dart +++ b/lib/DrawerPage.dart @@ -44,14 +44,17 @@ class _DrawerPageState extends State { final loginCtx = LoginContext.of(context); return Scaffold( - appBar: AppBar(title: Text(title), actions: [ - IconButton( - onPressed: () { - loginCtx.onLoggin(false); - Token.getInstance().setToken("", ""); - }, - icon: const Icon(Icons.logout)) - ],), + appBar: AppBar( + title: Text(title), + actions: [ + IconButton( + onPressed: () { + loginCtx.onLoggin(false); + Token.getInstance().setToken("", ""); + }, + icon: const Icon(Icons.logout)) + ], + ), body: body, drawer: Drawer( child: ListView(children: [ diff --git a/lib/app.dart b/lib/app.dart index dcac65a..d295f37 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -19,8 +19,8 @@ class App extends StatelessWidget { } else { return const MaterialApp( home: DrawerPage( - title: 'OpenMediaCenter', - )); + title: 'OpenMediaCenter', + )); } } } diff --git a/lib/login/login_screen.dart b/lib/login/login_screen.dart index e12c649..c8bd6c9 100644 --- a/lib/login/login_screen.dart +++ b/lib/login/login_screen.dart @@ -18,7 +18,6 @@ class _LoginScreenState extends State { final TextEditingController _domainTextController = TextEditingController(); final TextEditingController _passwordTextController = TextEditingController(); String error = ""; - bool _loginActive = false; Future login(String password, String domain) async { Log.i("logging in..."); @@ -64,7 +63,8 @@ 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, @@ -79,7 +79,8 @@ 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: [ @@ -123,7 +124,8 @@ 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, @@ -132,18 +134,23 @@ class _LoginScreenState extends State { color: Colors.white, onPressed: () async { Log.d("clickkked"); - final pwd = _passwordTextController.value.text; - final domain = _domainTextController.value.text; + final pwd = + _passwordTextController.value.text; + final domain = + _domainTextController.value.text; var err = ""; - if (domain.startsWith("https://") || domain.startsWith("http://")) { + if (domain.startsWith("https://") || + domain.startsWith("http://")) { err = await login(pwd, domain); if (err.isEmpty) return; } else { // try to auto infering domain prefix - err = await login(pwd, "https://" + domain); + err = await login( + pwd, "https://" + domain); if (err.isEmpty) return; - err = await login(pwd, "http://" + domain); + err = await login( + pwd, "http://" + domain); if (err.isEmpty) return; } diff --git a/lib/main.dart b/lib/main.dart index 39b3675..80b7c05 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,4 @@ -import "package:dart_vlc/dart_vlc.dart"; +import "package:dart_vlc/dart_vlc.dart"; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:openmediacentermobile/app.dart'; @@ -11,7 +11,6 @@ void main() async { Log.i("App init!"); DartVLC.initialize(); if (isDesktop()) { - } else { await loadDeviceInfo(); } diff --git a/lib/platform.dart b/lib/platform.dart index a352855..1802fbc 100644 --- a/lib/platform.dart +++ b/lib/platform.dart @@ -7,7 +7,8 @@ import 'package:flutter/foundation.dart'; bool _isTV = false; bool isDesktop() { - return (Platform.isLinux || Platform.isWindows || Platform.isMacOS) && !kIsWeb; + return (Platform.isLinux || Platform.isWindows || Platform.isMacOS) && + !kIsWeb; } Future loadDeviceInfo() async { diff --git a/lib/preview_grid.dart b/lib/preview_grid.dart index 722b2e2..1624da1 100644 --- a/lib/preview_grid.dart +++ b/lib/preview_grid.dart @@ -6,7 +6,12 @@ import 'package:openmediacentermobile/platform.dart'; import 'package:openmediacentermobile/preview_tile.dart'; class PreviewGrid extends StatefulWidget { - const PreviewGrid({Key? key, required this.videoLoader, this.headerBuilder, this.footerBuilder}) : super(key: key); + const PreviewGrid( + {Key? key, + required this.videoLoader, + this.headerBuilder, + this.footerBuilder}) + : super(key: key); final Future> Function() videoLoader; final Widget Function(_PreviewGridState state)? footerBuilder; @@ -37,79 +42,94 @@ class _PreviewGridState extends State { final double width = MediaQuery.of(context).size.width; return FutureBuilder>( - future: _data, // a previously-obtained Future or null + future: _data, builder: (BuildContext context, AsyncSnapshot> snapshot) { if (snapshot.hasError) { return Text("Error"); } else if (snapshot.hasData) { - return Stack( - children: [ - Column( - children: [ - if (widget.headerBuilder != null) widget.headerBuilder!(this), - Expanded( - child: MasonryGridView.count( - // every tile should be at max 330 pixels long... - crossAxisCount: isTV() ? width ~/ 200 : width ~/ 275, - // crossAxisCount: isTV() ? width ~/ 200 : width ~/ 330, - itemCount: snapshot.data!.length, - mainAxisSpacing: 4, - crossAxisSpacing: 4, - padding: EdgeInsets.all(5), - itemBuilder: (context, index) { - return PreviewTile( - dta: snapshot.data![index], - onLongPress: (img) { - setState(() { - _previewImage = img; - }); - }, - onLongPressEnd: () { - setState(() { - _previewImage = null; - }); - }, - ); - }, - ), - ), - if (widget.footerBuilder != null) widget.footerBuilder!(this), - ], - ), - if (_previewImage != null) ...[ - BackdropFilter( - filter: ImageFilter.blur( - sigmaX: 5.0, - sigmaY: 5.0, - ), - child: Container( - color: Colors.white.withOpacity(0.6), - ), - ), - Container( - child: Center( - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 50), - child: ClipRRect(borderRadius: BorderRadius.circular(10.0), child: _previewImage!)), - ), - ), - ], - ], - ); + return _mainGrid(snapshot.data!, width); } else { - return Column(children: const [ - SizedBox( - width: 60, - height: 60, - child: CircularProgressIndicator(), - ), - Padding( - padding: EdgeInsets.only(top: 16), - child: Text('Awaiting result...'), - ) - ]); + return _pageLoading(); } }, ); } + + Widget _pageLoading() { + return Column(children: const [ + SizedBox( + width: 60, + height: 60, + child: CircularProgressIndicator(), + ), + Padding( + padding: EdgeInsets.only(top: 16), + child: Text('Awaiting result...'), + ) + ]); + } + + Widget _mainGrid(List data, double width) { + return Stack( + children: [ + Column( + children: [ + if (widget.headerBuilder != null) widget.headerBuilder!(this), + Expanded( + child: MasonryGridView.count( + // every tile should be at max 330 pixels long... + crossAxisCount: isTV() ? width ~/ 200 : width ~/ 275, + // crossAxisCount: isTV() ? width ~/ 200 : width ~/ 330, + itemCount: data.length, + mainAxisSpacing: 4, + crossAxisSpacing: 4, + padding: EdgeInsets.all(5), + itemBuilder: (context, index) { + return PreviewTile( + dta: data[index], + onLongPress: (img) { + setState(() { + _previewImage = img; + }); + }, + onLongPressEnd: () { + setState(() { + _previewImage = null; + }); + }, + ); + }, + ), + ), + if (widget.footerBuilder != null) widget.footerBuilder!(this), + ], + ), + if (_previewImage != null) ..._buildPreviewImage(), + ], + ); + } + + List _buildPreviewImage() { + return [ + BackdropFilter( + filter: ImageFilter.blur( + sigmaX: 5.0, + sigmaY: 5.0, + ), + child: Container( + color: Colors.white.withOpacity(0.6), + ), + ), + Container( + child: Center( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 50), + child: ClipRRect( + borderRadius: BorderRadius.circular(10.0), + child: _previewImage!), + ), + ), + ), + ]; + } } diff --git a/lib/preview_tile.dart b/lib/preview_tile.dart index d3b6c47..60248de 100644 --- a/lib/preview_tile.dart +++ b/lib/preview_tile.dart @@ -17,12 +17,15 @@ 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()); } } class PreviewTile extends StatefulWidget { - const PreviewTile({Key? key, required this.dta, this.onLongPress, this.onLongPressEnd}) : super(key: key); + const PreviewTile( + {Key? key, required this.dta, this.onLongPress, this.onLongPressEnd}) + : super(key: key); final VideoT dta; final Function(Image img)? onLongPress; final Function? onLongPressEnd; @@ -53,7 +56,8 @@ class _PreviewTileState extends State { } Future loadData() async { - final data = await API.query("video", "readThumbnail", {'Movieid': widget.dta.id}); + final data = + await API.query("video", "readThumbnail", {'Movieid': widget.dta.id}); final img = Image.memory( base64Decode(data.substring(23)), @@ -99,17 +103,20 @@ class _PreviewTileState extends State { child: GestureDetector( behavior: HitTestBehavior.translucent, onLongPress: () { - if (widget.onLongPress != null) widget.onLongPress!(snapshot.data!); + if (widget.onLongPress != null) + widget.onLongPress!(snapshot.data!); }, onLongPressEnd: (details) { - if (widget.onLongPressEnd != null) widget.onLongPressEnd!(); + if (widget.onLongPressEnd != null) + widget.onLongPressEnd!(); }, child: InkWell( onTap: () { Navigator.push( context, MaterialPageRoute( - builder: (context) => VideoScreen(metaData: widget.dta), + builder: (context) => + VideoScreen(metaData: widget.dta), ), ); }, diff --git a/lib/shufflescreen.dart b/lib/shufflescreen.dart index 7995a5d..8457a3e 100644 --- a/lib/shufflescreen.dart +++ b/lib/shufflescreen.dart @@ -17,11 +17,13 @@ class ShuffleScreen extends StatefulWidget { class _ShuffleScreenState extends State { Future> loadData(int nr) async { - final data = await API.query("video", "getRandomMovies", {'Number': nr, 'Seed': Random().nextInt(0x7fffffff)}); + final data = await API.query("video", "getRandomMovies", + {'Number': nr, 'Seed': Random().nextInt(0x7fffffff)}); final d = jsonDecode(data); - List dta = (d['Videos'] as List).map((e) => VideoT.fromJson(e)).toList(); + List dta = + (d['Videos'] as List).map((e) => VideoT.fromJson(e)).toList(); return dta; } diff --git a/lib/video_feed.dart b/lib/video_feed.dart index 6b6384c..2ac6b1a 100644 --- a/lib/video_feed.dart +++ b/lib/video_feed.dart @@ -17,13 +17,13 @@ class VideoFeed extends StatefulWidget { } class VideoFeedState extends State { - 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(); + List dta = + (d['Videos'] as List).map((e) => VideoT.fromJson(e)).toList(); return dta; } @@ -33,6 +33,8 @@ class VideoFeedState extends State { double width = MediaQuery.of(context).size.width; Log.d(width); - return PreviewGrid(videoLoader: () => loadData(),); + return PreviewGrid( + videoLoader: () => loadData(), + ); } } diff --git a/lib/videoscreen_desktop.dart b/lib/videoscreen_desktop.dart index 381d824..db33e9b 100644 --- a/lib/videoscreen_desktop.dart +++ b/lib/videoscreen_desktop.dart @@ -10,7 +10,6 @@ import 'package:video_player/video_player.dart'; import 'api/api.dart'; import 'api/token.dart'; -import 'log/log.dart'; import 'platform.dart'; class VideoScreen extends StatefulWidget { @@ -22,11 +21,13 @@ class VideoScreen extends StatefulWidget { } class _VideoScreenState extends State { - Player? _player = isDesktop() ? Player(id: Random().nextInt(0x7fffffff)) : null; + Player? _player = + isDesktop() ? Player(id: Random().nextInt(0x7fffffff)) : null; ChewieController? _chewieController; void loadData() async { - final data = await API.query("video", "loadVideo", {'MovieId': widget.metaData.id}); + final data = + await API.query("video", "loadVideo", {'MovieId': widget.metaData.id}); final d = jsonDecode(data); @@ -46,18 +47,18 @@ class _VideoScreenState extends State { autoStart: true, // default ); } else { - final VideoPlayerController _controller = VideoPlayerController.network(path); + final VideoPlayerController _controller = + VideoPlayerController.network(path); await _controller.initialize(); _chewieController = ChewieController( - videoPlayerController: _controller, - autoPlay: true, - looping: true, - allowFullScreen: true, - allowMuting: true, - allowPlaybackSpeedChanging: true, - zoomAndPan: true - ); + videoPlayerController: _controller, + autoPlay: true, + looping: true, + allowFullScreen: true, + allowMuting: true, + allowPlaybackSpeedChanging: true, + zoomAndPan: true); setState(() {}); } @@ -67,17 +68,18 @@ class _VideoScreenState extends State { void initState() { super.initState(); - if(isDesktop()){ + if (isDesktop()) { RawKeyboard.instance.addListener((value) { if (value.logicalKey == LogicalKeyboardKey.arrowRight) { - _player?.seek(_player!.position.position! + const Duration(seconds: 5)); + _player + ?.seek(_player!.position.position! + const Duration(seconds: 5)); } else if (value.logicalKey == LogicalKeyboardKey.arrowLeft) { - _player?.seek(_player!.position.position! + const Duration(seconds: -5)); + _player + ?.seek(_player!.position.position! + const Duration(seconds: -5)); } }); } - loadData(); // todo hide appbar after some seonds @@ -106,10 +108,9 @@ class _VideoScreenState extends State { Widget videoDesktop() { return Video( - player: _player, - scale: 1.0, // default - showControls: true - ); + player: _player, + scale: 1.0, // default + showControls: true); } Widget videoNotDesktop() { diff --git a/lib/videoscreen_web.dart b/lib/videoscreen_web.dart index eaba720..50343a0 100644 --- a/lib/videoscreen_web.dart +++ b/lib/videoscreen_web.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'api/api.dart'; import 'api/token.dart'; +import 'log/log.dart'; class VideoScreen extends StatefulWidget { const VideoScreen({Key? key, required this.videoID}) : super(key: key); @@ -15,7 +16,8 @@ class VideoScreen extends StatefulWidget { class _VideoScreenState extends State { void loadData() async { - final data = await API.query("video", "loadVideo", {'MovieId': widget.videoID}); + final data = + await API.query("video", "loadVideo", {'MovieId': widget.videoID}); final d = jsonDecode(data); @@ -25,8 +27,8 @@ class _VideoScreenState extends State { final baseurl = token.domain; // todo not static middle path - final path = baseurl + "/videos/vids/" + url; - + final String path = baseurl + "/videos/vids/" + url; + Log.d(path); } @override @@ -44,9 +46,7 @@ class _VideoScreenState extends State { appBar: AppBar( title: const Text('Second Route'), ), - body: const Center( - child: Text("Todo to implement") - ), + body: const Center(child: Text("Todo to implement")), ); } } diff --git a/pubspec.lock b/pubspec.lock index 1da58ae..dea2a09 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -173,7 +173,7 @@ packages: name: flutter_secure_storage url: "https://pub.dartlang.org" source: hosted - version: "6.0.0" + version: "5.1.2" flutter_secure_storage_linux: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ddb3b26..2335915 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,7 +34,7 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 - flutter_secure_storage: ^6.0.0 + flutter_secure_storage: ^5.0.2 logger: ^1.1.0 http: ^0.13.4 flutter_staggered_grid_view: ^0.6.1 diff --git a/test/widget_test.dart b/test/widget_test.dart index a165868..9805f61 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -11,7 +11,5 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:openmediacentermobile/main.dart'; void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - - }); + testWidgets('Counter increments smoke test', (WidgetTester tester) async {}); }