From 9dc42635d023ec04c52afe54c680a8ca80bdc06d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96zcan=20O=C4=9Fuz?= Date: Thu, 17 Jun 2021 23:41:14 +0300 Subject: [PATCH] Beta RC1: Video player implemented --- lib/data/PeerTube.dart | 0 lib/i18n.dart | 38 ++++++++++ lib/main.dart | 19 ++++- lib/screens/About.dart | 17 +++-- lib/screens/Chat.dart | 72 +++++++++++++++--- lib/screens/HomePage.dart | 30 ++++++-- lib/screens/Live.dart | 43 ++++++----- lib/screens/Settings.dart | 11 +++ lib/screens/VideoPlayer.dart | 140 +++++++++++++++++++++++++++++++++++ lib/screens/Videos.dart | 93 +++++++++++++++++++++++ lib/widgets/Schedule.dart | 121 ++++++++++++++++++++++++++++-- 11 files changed, 534 insertions(+), 50 deletions(-) delete mode 100644 lib/data/PeerTube.dart create mode 100644 lib/i18n.dart create mode 100644 lib/screens/Settings.dart create mode 100644 lib/screens/VideoPlayer.dart create mode 100644 lib/screens/Videos.dart diff --git a/lib/data/PeerTube.dart b/lib/data/PeerTube.dart deleted file mode 100644 index e69de29..0000000 diff --git a/lib/i18n.dart b/lib/i18n.dart new file mode 100644 index 0000000..63883e9 --- /dev/null +++ b/lib/i18n.dart @@ -0,0 +1,38 @@ +import 'package:get/get.dart'; + +class Messages extends Translations { + @override + Map> get keys => { + 'en_US': { + 'chat': 'Chat', + 'live': 'Live', + 'schedule': 'Schedule', + 'videos': 'Videos', + 'about_app': 'About ÖzgürKon app', + 'app_description': 'Android app for ÖzgürKon, Developed in Flutter.', + 'view_readme': 'View README', + 'view_changelog': 'View changelog', + 'view_license': 'View license', + 'contributors': 'Contributors', + 'fs_licenses': 'Free software licenses', + 'loading': "Loading...", + 'next_year': "See you next year!", + }, + 'tr_TR': { + 'chat': 'Sohbet', + 'live': 'Canlı', + 'schedule': 'Program', + 'videos': 'Videolar', + 'about_app': 'ÖzgürKon uygulaması hakkında', + 'app_description': + 'ÖzgürKon için Android uygulaması, Flutter ile geliştirildi.', + 'view_readme': "README'yi görüntüle", + 'view_changelog': "Değişiklik özetini görüntüle", + 'view_license': 'Lisans', + 'contributors': 'Katkıda bulunanlar', + 'fs_licenses': 'Özgür yazılım lisansları', + 'loading': 'Yükleniyor...', + 'next_year': "Seneye görüşmek üzere!", + } + }; +} diff --git a/lib/main.dart b/lib/main.dart index 715b4d3..8160364 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; import 'package:material_color_generator/material_color_generator.dart'; +import 'package:ozgurkon_app/screens/VideoPlayer.dart'; import 'package:ozgurkon_app/screens/HomePage.dart'; +import 'package:get/get.dart'; +import 'i18n.dart'; const ozgurkon_renk = const Color(0xffc03e24); @@ -12,12 +15,26 @@ class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { - return MaterialApp( + return GetMaterialApp( title: 'ÖzgürKon', theme: ThemeData( primarySwatch: generateMaterialColor(color: Color(0xffc03e24)), ), home: HomePage(), + translations: Messages(), + locale: Get.deviceLocale, + fallbackLocale: Locale('en', 'US'), + initialRoute: '/', + getPages: [ + GetPage( + name: '/', + page: () => HomePage(), + ), + GetPage( + name: '/video/:uuid', + page: () => VideoView(), + ), + ], ); } } diff --git a/lib/screens/About.dart b/lib/screens/About.dart index 3b1c41f..4a31a8f 100644 --- a/lib/screens/About.dart +++ b/lib/screens/About.dart @@ -2,6 +2,7 @@ import 'package:about/about.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:get/get.dart'; String appName = "ÖzgürKon"; String version = "0.1.1"; @@ -21,10 +22,10 @@ class MyAbout extends StatelessWidget { 'year': DateTime.now().year.toString(), 'author': "Özgür Yazılım Derneği", }, - //title: Text('About'), + title: Text('about_app'.tr), applicationVersion: 'Version {{ version }}, build #{{ buildNumber }}', applicationDescription: Text( - "ÖzgürKon app test", + "app_description".tr, textAlign: TextAlign.justify, ), applicationIcon: Image.asset('assets/ozgurkon_disi.png'), @@ -32,26 +33,26 @@ class MyAbout extends StatelessWidget { children: [ MarkdownPageListTile( filename: 'README.md', - title: Text('View Readme'), + title: Text('view_readme'.tr), icon: Icon(Icons.all_inclusive), ), MarkdownPageListTile( filename: 'CHANGELOG.md', - title: Text('View Changelog'), + title: Text('view_changelog'.tr), icon: Icon(Icons.view_list), ), MarkdownPageListTile( filename: 'LICENSE.md', - title: Text('View License'), + title: Text('view_license'.tr), icon: Icon(Icons.description), ), MarkdownPageListTile( filename: 'CONTRIBUTORS.md', - title: Text('Contributors'), + title: Text('contributors'.tr), icon: Icon(Icons.groups), ), LicensesPageListTile( - title: Text('Free software Licenses'), + title: Text('fs_licenses'.tr), icon: Icon(Icons.favorite), ), ], @@ -59,7 +60,7 @@ class MyAbout extends StatelessWidget { if (isIos) { return CupertinoApp( - title: 'About this app', + title: 'about_app'.tr, home: aboutPage, theme: CupertinoThemeData( brightness: theme.brightness, diff --git a/lib/screens/Chat.dart b/lib/screens/Chat.dart index 5128a4f..61e73c1 100644 --- a/lib/screens/Chat.dart +++ b/lib/screens/Chat.dart @@ -1,24 +1,78 @@ -import 'dart:io'; +import 'dart:convert'; import 'package:flutter/material.dart'; -import 'package:webview_flutter/webview_flutter.dart'; +import 'package:flutter/services.dart' show rootBundle; +import 'package:flutter_chat_types/flutter_chat_types.dart' as types; +import 'package:flutter_chat_ui/flutter_chat_ui.dart'; +import 'package:uuid/uuid.dart'; + +class ChatPage extends StatefulWidget { + const ChatPage({Key? key}) : super(key: key); -class WebViewExample extends StatefulWidget { @override - WebViewExampleState createState() => WebViewExampleState(); + _ChatPageState createState() => _ChatPageState(); } -class WebViewExampleState extends State { +class _ChatPageState extends State { + List _messages = []; + final _user = const types.User(id: '06c33e8b-e835-4736-80f4-63f44b66666c'); + @override void initState() { super.initState(); - // Enable hybrid composition. - if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView(); + _loadMessages(); + } + + void _addMessage(types.Message message) { + setState(() { + _messages.insert(0, message); + }); + } + + void _handlePreviewDataFetched( + types.TextMessage message, + types.PreviewData previewData, + ) { + final index = _messages.indexWhere((element) => element.id == message.id); + final updatedMessage = _messages[index].copyWith(previewData: previewData); + + WidgetsBinding.instance?.addPostFrameCallback((_) { + setState(() { + _messages[index] = updatedMessage; + }); + }); + } + + void _handleSendPressed(types.PartialText message) { + final textMessage = types.TextMessage( + author: _user, + createdAt: DateTime.now().millisecondsSinceEpoch, + id: const Uuid().v4(), + text: message.text, + ); + + _addMessage(textMessage); + } + + void _loadMessages() async { + final response = await rootBundle.loadString('assets/messages.json'); + final messages = (jsonDecode(response) as List) + .map((e) => types.Message.fromJson(e as Map)) + .toList(); + + setState(() { + _messages = messages; + }); } @override Widget build(BuildContext context) { - return WebView( - initialUrl: 'https://kiwiirc.com/client/chat.freenode.net/%23ozgurkon', + return Scaffold( + body: Chat( + messages: _messages, + onPreviewDataFetched: _handlePreviewDataFetched, + onSendPressed: _handleSendPressed, + user: _user, + ), ); } } diff --git a/lib/screens/HomePage.dart b/lib/screens/HomePage.dart index 0c4697b..06df8ae 100644 --- a/lib/screens/HomePage.dart +++ b/lib/screens/HomePage.dart @@ -1,7 +1,10 @@ import 'package:flutter/material.dart'; -import 'package:ozgurkon_app/screens/About.dart'; +import 'package:get/get.dart'; +import 'package:ozgurkon_app/screens/Live.dart'; import 'package:ozgurkon_app/screens/Chat.dart'; import 'package:ozgurkon_app/widgets/Schedule.dart'; +import 'package:ozgurkon_app/screens/Settings.dart'; +import 'package:ozgurkon_app/screens/Videos.dart'; class HomePage extends StatefulWidget { @override @@ -13,32 +16,45 @@ class HomePage extends StatefulWidget { class _HomeState extends State { int _currentIndex = 0; final List _children = [ - PlaceholderWidget(Colors.white), - MyAbout(), - WebViewExample() + ChatPage(), + Live(), + Schedule(), + VideosListView(), ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('ÖzgürKon'), + actions: [ + IconButton( + icon: const Icon(Icons.settings), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => Settings()), + ); + }) + ], ), body: _children[_currentIndex], // new bottomNavigationBar: BottomNavigationBar( + type: BottomNavigationBarType.fixed, onTap: onTabTapped, // new currentIndex: _currentIndex, // this will be set when a new tab is tapped items: [ BottomNavigationBarItem( icon: new Icon(Icons.chat), - label: 'Chat', + label: 'chat'.tr, ), BottomNavigationBarItem( icon: new Icon(Icons.live_tv), - label: 'Live', + label: 'live'.tr, ), BottomNavigationBarItem( - icon: Icon(Icons.date_range), label: 'Schedule') + icon: Icon(Icons.date_range), label: 'schedule'.tr), + BottomNavigationBarItem(icon: Icon(Icons.movie), label: 'videos'.tr) ], ), ); diff --git a/lib/screens/Live.dart b/lib/screens/Live.dart index 74897b9..fea0b03 100644 --- a/lib/screens/Live.dart +++ b/lib/screens/Live.dart @@ -1,28 +1,31 @@ import 'package:flutter/material.dart'; -import 'package:video_viewer/video_viewer.dart'; +import 'package:get/get.dart'; +import 'package:flutter_svg_provider/flutter_svg_provider.dart'; -class HLSVideoExample extends StatelessWidget { +class Live extends StatelessWidget { @override Widget build(BuildContext context) { - return FutureBuilder>( - future: VideoSource.fromM3u8PlaylistUrl( - "https://sfux-ext.sfux.info/hls/chapter/105/1588724110/1588724110.m3u8", - formatter: (quality) => - quality == "Auto" ? "Automatic" : "${quality.split("x").last}p", + return Container( + width: double.infinity, + height: double.infinity, + decoration: BoxDecoration( + color: Color(0xFFededed), + image: DecorationImage( + image: Svg( + 'assets/flat-mountains.svg', + ), + alignment: Alignment.bottomCenter, + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'next_year'.tr, + style: TextStyle(fontSize: 24, fontWeight: FontWeight.w700), + ) + ], ), - builder: (_, data) { - return data.hasData - ? VideoViewer( - source: data.data, - onFullscreenFixLandscape: true, - style: VideoViewerStyle( - thumbnail: Image.network( - "https://play-lh.googleusercontent.com/aA2iky4PH0REWCcPs9Qym2X7e9koaa1RtY-nKkXQsDVU6Ph25_9GkvVuyhS72bwKhN1P", - ), - ), - ) - : CircularProgressIndicator(); - }, ); } } diff --git a/lib/screens/Settings.dart b/lib/screens/Settings.dart new file mode 100644 index 0000000..b353276 --- /dev/null +++ b/lib/screens/Settings.dart @@ -0,0 +1,11 @@ +import 'package:flutter/material.dart'; +import 'package:ozgurkon_app/screens/About.dart'; + +class Settings extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + body: MyAbout(), + ); + } +} diff --git a/lib/screens/VideoPlayer.dart b/lib/screens/VideoPlayer.dart new file mode 100644 index 0000000..88a84d9 --- /dev/null +++ b/lib/screens/VideoPlayer.dart @@ -0,0 +1,140 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'package:get/get.dart'; +import 'package:fluttericon/entypo_icons.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:better_player/better_player.dart'; + +class Video { + final String uuid; + final String name; + final String desc; + final String speaker; + final String lang; + final int duration; + final String streamURL; + final String downloadURL; + + Video( + {required this.uuid, + required this.name, + required this.desc, + required this.speaker, + required this.lang, + required this.duration, + required this.streamURL, + required this.downloadURL}); + + factory Video.fromJson(Map json) { + return Video( + uuid: json['uuid'], + name: json['name'].split(' | ')[0].split(' by ')[0], + desc: json['description'], + speaker: json['name'].split(' | ')[0].split(' by ')[1], + lang: json['language']['id'], + duration: json['duration'], + streamURL: json['files'][0]['fileUrl'], + downloadURL: json['files'][0]['fileDownloadUrl'], + ); + } +} + +class VideoView extends StatelessWidget { + @override + Widget build(BuildContext context) { + return FutureBuilder