From 3935d8ffbc4ccf74c5f7f0e8c603aeeab86c627b Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Wed, 28 Apr 2021 15:25:41 +0300 Subject: [PATCH] voice recording added and improvements --- android/app/src/main/AndroidManifest.xml | 1 + assets/icons/mic_icon.svg | 10 + assets/icons/record.svg | 20 +- assets/icons/recording_off.svg | 17 + assets/icons/recording_on.svg | 30 ++ lib/classes/consts.dart | 1 + lib/main.dart | 2 +- lib/models/aya_tangheem_property.dart | 8 +- lib/models/aya_tangheem_type_mapped.dart | 8 +- lib/ui/common_appbar.dart | 38 +- lib/ui/misc/no_data_ui.dart | 2 +- lib/ui/screens/home_screen.dart | 14 +- lib/ui/screens/quran_screen.dart | 124 ++++--- lib/ui/screens/tangheem_detail_screen.dart | 412 +++++++++++++-------- lib/ui/screens/tangheem_screen.dart | 10 +- lib/widgets/aya_player_widget.dart | 49 ++- lib/widgets/aya_record_widget.dart | 311 ++++++++++++++++ lib/widgets/text_highlight_widget.dart | 4 +- pubspec.yaml | 1 + 19 files changed, 793 insertions(+), 269 deletions(-) create mode 100644 assets/icons/mic_icon.svg create mode 100644 assets/icons/recording_off.svg create mode 100644 assets/icons/recording_on.svg create mode 100644 lib/widgets/aya_record_widget.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index b6c1eb1..c4fd9d0 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -8,6 +8,7 @@ + + + + + + + + + + diff --git a/assets/icons/record.svg b/assets/icons/record.svg index 24a2adb..36278d6 100644 --- a/assets/icons/record.svg +++ b/assets/icons/record.svg @@ -1,14 +1,14 @@ - + - - - - - - - - - + + + + + + + + + diff --git a/assets/icons/recording_off.svg b/assets/icons/recording_off.svg new file mode 100644 index 0000000..3e8e861 --- /dev/null +++ b/assets/icons/recording_off.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/assets/icons/recording_on.svg b/assets/icons/recording_on.svg new file mode 100644 index 0000000..fcd0dde --- /dev/null +++ b/assets/icons/recording_on.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index 897f46c..e80ce36 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -12,4 +12,5 @@ class GlobalConsts { static String email = "email"; static String password = "password"; static String bookmark = "bookmark"; + static String fontZoomSize = "font_zoom_size"; } diff --git a/lib/main.dart b/lib/main.dart index 463f518..975320b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -59,7 +59,7 @@ class Application extends StatelessWidget { break; case TangheemDetailScreen.routeName: className = CommonAppbar( - child: TangheemDetailScreen(ayatTangheemTypeMappedData: settings.arguments), + child: TangheemDetailScreen(ayatTangheemTypeMappedDataList: settings.arguments), ); break; case QuranScreen.routeName: diff --git a/lib/models/aya_tangheem_property.dart b/lib/models/aya_tangheem_property.dart index b9f8124..1d7cfb0 100644 --- a/lib/models/aya_tangheem_property.dart +++ b/lib/models/aya_tangheem_property.dart @@ -32,7 +32,7 @@ class AyaTangheemProperty { } class AyaTangheemPropertyData { - String ayaTangheemPropertyId; + String ayaTangheemTypePropertyId; String ayaTangheemTypeId; String tangheemTypePropertyId; String propertyValue; @@ -46,7 +46,7 @@ class AyaTangheemPropertyData { int orderNo; AyaTangheemPropertyData( - {this.ayaTangheemPropertyId, + {this.ayaTangheemTypePropertyId, this.ayaTangheemTypeId, this.tangheemTypePropertyId, this.propertyValue, @@ -60,7 +60,7 @@ class AyaTangheemPropertyData { this.orderNo}); AyaTangheemPropertyData.fromJson(Map json) { - ayaTangheemPropertyId = json['ayaTangheemPropertyId']; + ayaTangheemTypePropertyId = json['ayaTangheemTypePropertyId']; ayaTangheemTypeId = json['ayaTangheemTypeId']; tangheemTypePropertyId = json['tangheemTypePropertyId']; propertyValue = json['propertyValue']; @@ -76,7 +76,7 @@ class AyaTangheemPropertyData { Map toJson() { final Map data = new Map(); - data['ayaTangheemPropertyId'] = this.ayaTangheemPropertyId; + data['ayaTangheemTypePropertyId'] = this.ayaTangheemTypePropertyId; data['ayaTangheemTypeId'] = this.ayaTangheemTypeId; data['tangheemTypePropertyId'] = this.tangheemTypePropertyId; data['propertyValue'] = this.propertyValue; diff --git a/lib/models/aya_tangheem_type_mapped.dart b/lib/models/aya_tangheem_type_mapped.dart index 81ebed8..2c43bb6 100644 --- a/lib/models/aya_tangheem_type_mapped.dart +++ b/lib/models/aya_tangheem_type_mapped.dart @@ -125,17 +125,17 @@ class TangheemProperty { String propertyText; bool isInsideTable; int orderNo; - String ayaTangheemPropertyId; + String ayaTangheemTypePropertyId; String propertyValue; - TangheemProperty({this.tangheemTypePropertyId, this.propertyText, this.isInsideTable, this.orderNo, this.ayaTangheemPropertyId, this.propertyValue}); + TangheemProperty({this.tangheemTypePropertyId, this.propertyText, this.isInsideTable, this.orderNo, this.ayaTangheemTypePropertyId, this.propertyValue}); TangheemProperty.fromJson(Map json) { tangheemTypePropertyId = json['tangheemTypePropertyId']; propertyText = json['propertyText']; isInsideTable = json['isInsideTable']; orderNo = json['orderNo']; - ayaTangheemPropertyId = json['ayaTangheemPropertyId']; + ayaTangheemTypePropertyId = json['ayaTangheemTypePropertyId']; propertyValue = json['propertyValue']; } @@ -145,7 +145,7 @@ class TangheemProperty { data['propertyText'] = this.propertyText; data['isInsideTable'] = this.isInsideTable; data['orderNo'] = this.orderNo; - data['ayaTangheemPropertyId'] = this.ayaTangheemPropertyId; + data['ayaTangheemTypePropertyId'] = this.ayaTangheemTypePropertyId; data['propertyValue'] = this.propertyValue; return data; } diff --git a/lib/ui/common_appbar.dart b/lib/ui/common_appbar.dart index 94e1d91..dfee22b 100644 --- a/lib/ui/common_appbar.dart +++ b/lib/ui/common_appbar.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:tangheem/api/tangheem_user_api_client.dart'; import 'package:tangheem/app_state/app_state.dart'; import 'package:tangheem/classes/colors.dart'; +import 'package:tangheem/classes/consts.dart'; import 'package:tangheem/classes/utils.dart'; import 'package:tangheem/models/quick_links_model.dart'; import 'package:tangheem/ui/screens/bookmark_screen.dart'; @@ -31,9 +33,10 @@ class _CommonAppbarState extends State { void initState() { super.initState(); getQuickLinks(); + getPrefs(); } - getQuickLinks() async { + void getQuickLinks() async { if (widget.showDrawer) { try { quickLinks = (await TangheemUserApiClient().quickLinks())?.data ?? []; @@ -43,6 +46,13 @@ class _CommonAppbarState extends State { } } + int fontSize; + SharedPreferences prefs; + void getPrefs() async { + prefs = await SharedPreferences.getInstance(); + fontSize = prefs.getInt(GlobalConsts.fontZoomSize) ?? 18; + } + @override void dispose() { super.dispose(); @@ -122,17 +132,31 @@ class _CommonAppbarState extends State { ), Container( margin: EdgeInsets.only(top: 8, bottom: 16), - padding: EdgeInsets.only(left: 12, right: 12), + padding: EdgeInsets.only(left: 16, right: 16), child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - commonIconButton("assets/icons/accessibility.svg", () {}), - commonIconButton("assets/icons/dark_mode.svg", () {}), commonIconButton("assets/icons/bookmark.svg", () { Navigator.pushNamed(context, BookmarkScreen.routeName); }), - commonIconButton("assets/icons/increase_size.svg", () {}), - commonIconButton("assets/icons/reduce_size.svg", () {}), - commonIconButton("assets/icons/notification.svg", () {}), + commonIconButton("assets/icons/increase_size.svg", () { + if (fontSize >= 36) { + Utils.showToast("وصل حجم الخط إلى الحد الأقصى للحجم"); + return; + } + fontSize += 2; + prefs.setInt(GlobalConsts.fontZoomSize, fontSize); + Utils.showToast("زيادة حجم الخط"); + }), + commonIconButton("assets/icons/reduce_size.svg", () { + if (fontSize <= 12) { + Utils.showToast("وصل حجم الخط إلى الحد الأدنى للحجم"); + return; + } + fontSize -= 2; + prefs.setInt(GlobalConsts.fontZoomSize, fontSize); + Utils.showToast("تم تقليل حجم الخط"); + }), commonIconButton("assets/icons/user_logged.svg", () { if (AppState().isUserLogin) { Utils.showToast("أنت بالفعل تسجيل الدخول"); diff --git a/lib/ui/misc/no_data_ui.dart b/lib/ui/misc/no_data_ui.dart index 2d5c010..f0f4c82 100644 --- a/lib/ui/misc/no_data_ui.dart +++ b/lib/ui/misc/no_data_ui.dart @@ -15,7 +15,7 @@ class NoDataUI extends StatelessWidget { SvgPicture.asset("assets/icons/no_data.svg", width: 75, height: 75), SizedBox(height: 8), Text( - "آسف! لا تتوافر بيانات", + "لا توجد بيانات", style: TextStyle(fontSize: 16, color: ColorConsts.primaryBlue.withOpacity(0.7), fontWeight: FontWeight.w600), ), SizedBox(height: 16), diff --git a/lib/ui/screens/home_screen.dart b/lib/ui/screens/home_screen.dart index 3439f3c..0604f8d 100644 --- a/lib/ui/screens/home_screen.dart +++ b/lib/ui/screens/home_screen.dart @@ -95,7 +95,7 @@ class _HomeScreenState extends State { Row( children: [ Expanded( - child: CommonDropDownButton(_selectedTangheemType, hintText: "الأسلوب اللغوي", list: _tangheemList, onSelect: (index) { + child: CommonDropDownButton(_selectedTangheemType, hintText: "اختر الأسلوب اللغوي", list: _tangheemList, onSelect: (index) { if (_selectedTangheemType != index) { setState(() { _selectedTangheemType = index; @@ -105,7 +105,7 @@ class _HomeScreenState extends State { ), SizedBox(width: 8), Expanded( - child: CommonDropDownButton(_selectedSurah, hintText: "السورة", list: _surahList, onSelect: (index) { + child: CommonDropDownButton(_selectedSurah, hintText: "اختر السورة", list: _surahList, onSelect: (index) { if (_selectedSurah != index) { setState(() { _selectedSurah = index; @@ -121,11 +121,11 @@ class _HomeScreenState extends State { highlightColor: Colors.transparent, onTap: () async { if (_selectedTangheemType < 0) { - Utils.showToast("الرجاء اختيار نوع تنغيم"); + Utils.showToast("الرجائ تعبئة جميع القوائم"); return; } if (_selectedSurah < 0) { - Utils.showToast("الرجاء اختيار السورة"); + Utils.showToast("الرجائ تعبئة جميع القوائم"); return; } _searchFocusNode.unfocus(); @@ -147,7 +147,7 @@ class _HomeScreenState extends State { mainAxisSize: MainAxisSize.min, children: [ Text( - "ابدأ البحث", + "بحث", maxLines: 1, style: TextStyle(fontSize: 12, color: Colors.white), ), @@ -171,7 +171,7 @@ class _HomeScreenState extends State { fillColor: Colors.white, filled: true, hintStyle: TextStyle(color: ColorConsts.textHintGrey, fontSize: 12), - hintText: "البحث عن سورة أو آية", + hintText: "البحث عن آية", prefixIconConstraints: BoxConstraints(maxHeight: 16), prefixIcon: Padding( padding: EdgeInsets.only(right: 6), @@ -181,7 +181,7 @@ class _HomeScreenState extends State { onTap: () async { _searchFocusNode.unfocus(); if (_searchController.text.length < 1) { - Utils.showToast("يجب إدخال بعض الكلمات للبحث"); + Utils.showToast("الرجاء ادخال محتوى للبحث"); return; } _searchFocusNode.canRequestFocus = false; diff --git a/lib/ui/screens/quran_screen.dart b/lib/ui/screens/quran_screen.dart index 90a78b8..d1f635d 100644 --- a/lib/ui/screens/quran_screen.dart +++ b/lib/ui/screens/quran_screen.dart @@ -3,9 +3,11 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:tangheem/api/tangheem_user_api_client.dart'; import 'package:tangheem/app_state/app_state.dart'; import 'package:tangheem/classes/colors.dart'; +import 'package:tangheem/classes/consts.dart'; import 'package:tangheem/classes/utils.dart'; import 'package:tangheem/models/aya_model.dart'; import 'package:tangheem/models/aya_tangheem_type_mapped.dart'; @@ -56,9 +58,19 @@ class _QuranScreenState extends State { super.initState(); scrollToId = ScrollToId(scrollController: scrollController); getBookMark(); + getPrefs(); getSurah(); } + double fontSize = 18; + + SharedPreferences prefs; + void getPrefs() async { + prefs = await SharedPreferences.getInstance(); + fontSize = (prefs.getInt(GlobalConsts.fontZoomSize) ?? 18) + 0.0; + setState(() {}); + } + void getSurah() async { try { if (AppState().getSurahModel == null) { @@ -92,11 +104,13 @@ class _QuranScreenState extends State { } int numberOfAyah = 0; + var filteredAyahList = []; void filterData() { numberOfAyah = _surahModel?.data[_selectedSurah]?.numberOfAyahs ?? 0; - var filteredAyahList = List.generate(getNextMultiple(numberOfAyah), (index) => index + 1).toList().where((element) => element == 1 || (element % 5) == 0).toList() ?? []; + filteredAyahList = List.generate(getNextMultiple(numberOfAyah), (index) => index + 1).toList().where((element) => element == 1 || (element % 5) == 0).toList() ?? []; _fromAyaList = filteredAyahList.getRange(0, filteredAyahList.length - 1)?.toList() ?? []; - _toAyaList = filteredAyahList.getRange(1, filteredAyahList.length)?.toList() ?? []; + + // _toAyaList = filteredAyahList.getRange(1, filteredAyahList.length)?.toList() ?? []; // _currentPage = 0; // _selectedFromAya = 0; // _selectedToAya = 0; @@ -222,11 +236,12 @@ class _QuranScreenState extends State { Row( children: [ Expanded( - child: CommonDropDownButton(_selectedSurah, hintText: "السورة", list: _surahList, onSelect: (index) { + child: CommonDropDownButton(_selectedSurah, hintText: "اختر السورة", list: _surahList, onSelect: (index) { if (_selectedSurah != index) { _selectedSurah = index; _selectedToAya = -1; _selectedFromAya = -1; + _toAyaList = []; filterData(); } }), @@ -236,6 +251,9 @@ class _QuranScreenState extends State { child: CommonDropDownButton(_selectedFromAya, hintText: "من الاية", list: _fromAyaList.map((e) => "من الاية" + " $e").toList(), onSelect: (index) { if (_selectedFromAya != index) { _selectedFromAya = index; + var abc = filteredAyahList.indexOf(_selectedFromAya); + print("index:$abc"); + _toAyaList = filteredAyahList.getRange(_selectedFromAya + 1, filteredAyahList.length)?.toList() ?? []; setState(() {}); } }), @@ -254,9 +272,9 @@ class _QuranScreenState extends State { SizedBox(height: 8), Row( children: [ - commonIconButton("ابدأ البحث", "assets/icons/go_forward.svg", () { + commonIconButton("عرض", "assets/icons/go_forward.svg", () { if (_selectedSurah < 0) { - Utils.showToast("الرجاء اختيار السورة"); + Utils.showToast("يرجى اختيار السورة"); return; } else { if (_selectedFromAya < 0 && _selectedToAya < 0) { @@ -270,15 +288,15 @@ class _QuranScreenState extends State { } } }), - SizedBox(width: 8), - commonIconButton( - "مسح البحث", - "assets/icons/cancel.svg", - (_selectedSurah == -1 || _selectedFromAya == -1 || _selectedToAya == -1) - ? null - : () { - _clearFilterAndRefreshData(); - }), + // SizedBox(width: 8), + // commonIconButton( + // "مسح البحث", + // "assets/icons/cancel.svg", + // (_selectedSurah == -1 || _selectedFromAya == -1 || _selectedToAya == -1) + // ? null + // : () { + // _clearFilterAndRefreshData(); + // }), ], ), SizedBox(height: 16), @@ -372,21 +390,6 @@ class _QuranScreenState extends State { ), ), ), - // SizedBox(height: 8), - // AyaScrollViewer( - // scrollDirection: Axis.horizontal, - // scrollToId: scrollToId, - // children: [ - // for (int i = 0; i < _fromAyaList.length; i++) - // ScrollContent( - // id: '$i', - // child: Text( - // " ${i + 1} ", - // style: TextStyle(fontSize: 16, color: _currentPage == i ? ColorConsts.secondaryOrange : ColorConsts.textHintGrey), - // ), - // ), - // ], - // ), SizedBox(height: 4), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -471,7 +474,7 @@ class _QuranScreenState extends State { ); } - void _selectTangheemType(List list) async { + void _selectTangheemType(List list, List dataList) async { SimpleDialog dialog = SimpleDialog( title: Text( 'اختر نوع تنغيم', @@ -481,19 +484,20 @@ class _QuranScreenState extends State { children: list .map((e) => SimpleDialogOption( child: Text( - e.tangheemTypeName, + e, textDirection: TextDirection.rtl, style: TextStyle(color: ColorConsts.primaryBlue), ), onPressed: () { Navigator.pop(context); - Navigator.pushNamed(context, TangheemDetailScreen.routeName, arguments: e); + var list = _ayatTangheemTypeMapped?.data?.where((element) => element.tangheemTypeName == e)?.toList() ?? []; + Navigator.pushNamed(context, TangheemDetailScreen.routeName, arguments: list); }, )) .toList(), ); - showDialog( + await showDialog( context: context, builder: (BuildContext context) { return dialog; @@ -536,7 +540,7 @@ class _QuranScreenState extends State { style: TextStyle( backgroundColor: _selectedAyaForBookmark?.ayahID == element.ayahID ? ColorConsts.secondaryOrange.withOpacity(.4) : Colors.transparent, fontFamily: "UthmanicHafs", - fontSize: 18, + fontSize: fontSize, color: ColorConsts.primaryBlue, fontWeight: FontWeight.bold, ), @@ -545,7 +549,7 @@ class _QuranScreenState extends State { setState(() { if (_selectedAyaForBookmark == null) { _selectedAyaForBookmark = BookMarkModel.fromJson(element.toJson()); - addBookMarkOption(); + showAyaOptions(); return; } _selectedAyaForBookmark = null; @@ -561,7 +565,7 @@ class _QuranScreenState extends State { text: "\n" + "$_currentPage", style: TextStyle( fontFamily: "UthmanicHafs", - fontSize: 18, + fontSize: fontSize, color: ColorConsts.primaryBlue, fontWeight: FontWeight.bold, ), @@ -579,8 +583,7 @@ class _QuranScreenState extends State { ); } - void addBookMarkOption() { - var isAyaHaveTagheem = _ayatTangheemTypeMapped?.data?.firstWhere((element) => element.ayahNo == _selectedAyaForBookmark.ayahID, orElse: () => null); + void showAyaOptions() { showModalBottomSheet( context: context, backgroundColor: Colors.transparent, @@ -600,36 +603,45 @@ class _QuranScreenState extends State { child: Column( mainAxisSize: MainAxisSize.min, children: [ - if (isAyaHaveTagheem != null) - InkWell( - onTap: () { - Navigator.pop(context); - List _ayatList = _ayatTangheemTypeMapped.data?.where((element) => element.ayahNo == _selectedAyaForBookmark.ayahID)?.toList() ?? []; - if (_ayatList.length > 1) { - _selectTangheemType(_ayatList); - } else { - Navigator.pushNamed(context, TangheemDetailScreen.routeName, arguments: _ayatList.first); - } - }, - child: Row( - children: [Text("عرض تنغيم")], - ), + InkWell( + onTap: () async { + Navigator.pop(context); + List list = []; + + list = _ayatTangheemTypeMapped?.data?.where((element) => element.ayahNo == _selectedAyaForBookmark.ayahID)?.toList() ?? []; + if (list.isEmpty) { + Utils.showToast("لا توجد أساليب تنغيم في هذه الأية"); + return; + } + + List tempList = list.map((element) => element.tangheemTypeName).toList().toSet().toList(); + + if (tempList.length > 1) { + _selectTangheemType(tempList, list); + return; + } + Navigator.pushNamed(context, TangheemDetailScreen.routeName, arguments: list); + }, + child: Row( + children: [Text("تفاصيل التنغيم")], ), - if (isAyaHaveTagheem != null) SizedBox(height: 8), + ), + SizedBox(height: 8), InkWell( onTap: () { Navigator.pop(context); var temp = _bookMark.firstWhere((element) => element.ayahID == _selectedAyaForBookmark.ayahID, orElse: () => null); if (temp != null) { - Utils.showToast("تمت إضافة إشارة مرجعية بالفعل"); + // + Utils.showToast("هذه الأية مضافة سابقا"); return; } _bookMark.add(_selectedAyaForBookmark); BookMarkModel.saveToPrefs(_bookMark); - Utils.showToast("وأضاف المرجعية"); + Utils.showToast("تم اضافة الأية كمرجع"); }, child: Row( - children: [Text("أضف إلى المرجعية")], + children: [Text("اضافة الأية كمرجع؟")], ), ), ], diff --git a/lib/ui/screens/tangheem_detail_screen.dart b/lib/ui/screens/tangheem_detail_screen.dart index 81cb1e0..55ccd43 100644 --- a/lib/ui/screens/tangheem_detail_screen.dart +++ b/lib/ui/screens/tangheem_detail_screen.dart @@ -1,23 +1,27 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:tangheem/api/admin_configuration_api_client.dart'; import 'package:tangheem/api/tangheem_user_api_client.dart'; import 'package:tangheem/app_state/app_state.dart'; import 'package:tangheem/classes/colors.dart'; +import 'package:tangheem/classes/consts.dart'; import 'package:tangheem/classes/utils.dart'; import 'package:tangheem/models/aya_tangheem_type_mapped.dart'; import 'package:tangheem/models/discussion_model.dart'; import 'package:tangheem/ui/dialogs/discussion_input_dialog.dart'; import 'package:tangheem/widgets/aya_player_widget.dart'; +import 'package:tangheem/widgets/aya_record_widget.dart'; import 'package:tangheem/widgets/text_highlight_widget.dart'; import 'package:tangheem/extensions/string_extensions.dart'; import 'login_screen.dart'; class TangheemDetailScreen extends StatefulWidget { static const String routeName = "/tangheem_detail"; - final AyatTangheemTypeMappedData ayatTangheemTypeMappedData; - TangheemDetailScreen({Key key, this.ayatTangheemTypeMappedData}) : super(key: key); + final List ayatTangheemTypeMappedDataList; + // final AyatTangheemTypeMappedData ayatTangheemTypeMappedData; + TangheemDetailScreen({Key key, this.ayatTangheemTypeMappedDataList}) : super(key: key); @override _TangheemDetailScreenState createState() { @@ -27,29 +31,54 @@ class TangheemDetailScreen extends StatefulWidget { class _TangheemDetailScreenState extends State { GlobalKey _globalKey = GlobalKey(); - List _tangheemInsideTableTrueList = []; - List _tangheemInsideTableValueList = []; - List _tangheemInsideTableEmptyList = []; - List _tangheemWords = []; + + List voiceNoteList = []; + + List ayatTangheemTypeMappedDataList = []; int _discussionPage = -1; - AyatTangheemTypeMapped _ayatTangheemTypeMapped; - AyatTangheemTypeMappedData _ayatTangheemTypeMappedData; + AyatTangheemTypeMappedData _ayatTangheemTypeMappedFirstData; DiscussionModel _discussionModel; @override void initState() { super.initState(); - _tangheemInsideTableValueList = []; - _tangheemInsideTableEmptyList = []; - filterData(); + + ayatTangheemTypeMappedDataList = widget.ayatTangheemTypeMappedDataList; + _ayatTangheemTypeMappedFirstData = ayatTangheemTypeMappedDataList.first; + filterVoiceListData(); + getPrefs(); getTangheemDiscussion(); } + double fontSize = 18; + + SharedPreferences prefs; + void getPrefs() async { + prefs = await SharedPreferences.getInstance(); + fontSize = (prefs.getInt(GlobalConsts.fontZoomSize) ?? 18) + 0.0; + setState(() {}); + } + + String getArabicIndexWord(int index) { + if (index == 0) { + return 'الأولى'; + } else if (index == 1) { + return 'الثانية'; + } else if (index == 2) { + return 'الثالثة'; + } else if (index == 3) { + return 'الرابعة'; + } else if (index == 4) { + return 'الخامسة'; + } + return ""; + } + void getTangheemDiscussion() async { Utils.showLoading(context); try { - _discussionModel = await TangheemUserApiClient().getDiscussionByTangheemID(_discussionPage, widget.ayatTangheemTypeMappedData.ayaTangheemTypeId); + _discussionModel = await TangheemUserApiClient().getDiscussionByTangheemID(_discussionPage, _ayatTangheemTypeMappedFirstData.ayaTangheemTypeId); Utils.hideLoading(context); setState(() {}); } catch (ex, tr) { @@ -61,7 +90,7 @@ class _TangheemDetailScreenState extends State { void sendComment(String discussionText) async { Utils.showLoading(context); try { - await AdminConfigurationApiClient().addDiscussion(discussionText, widget.ayatTangheemTypeMappedData.ayaTangheemTypeId); + await AdminConfigurationApiClient().addDiscussion(discussionText, _ayatTangheemTypeMappedFirstData.ayaTangheemTypeId); Utils.showToast("تم إرسال التعليق ، سيكون مرئيًا بمجرد موافقة المسؤول عليه"); Utils.hideLoading(context); Navigator.pop(context); @@ -72,12 +101,10 @@ class _TangheemDetailScreenState extends State { } } - void filterData() { - _ayatTangheemTypeMappedData = widget.ayatTangheemTypeMappedData; - _tangheemWords.add(_ayatTangheemTypeMappedData.highlightText ?? ""); - _tangheemInsideTableTrueList = _ayatTangheemTypeMappedData?.property?.where((element) => (element.isInsideTable) && (element.propertyValue ?? "").isNotEmpty)?.toList() ?? []; - _tangheemInsideTableValueList = _ayatTangheemTypeMappedData?.property?.where((element) => (!element.isInsideTable) && (element.propertyValue ?? "").isNotEmpty)?.toList() ?? []; - // _tangheemInsideTableEmptyList = _ayatTangheemTypeMappedData?.property?.where((element) => (!element.isInsideTable) && (element.propertyValue ?? "").isEmpty)?.toList() ?? []; + void filterVoiceListData() { + ayatTangheemTypeMappedDataList.forEach((element) { + voiceNoteList.addAll(element.voiceNote); + }); } @override @@ -90,13 +117,13 @@ class _TangheemDetailScreenState extends State { return Container( padding: EdgeInsets.fromLTRB(16, 24, 16, 0), width: double.infinity, - child: _ayatTangheemTypeMappedData == null + child: _ayatTangheemTypeMappedFirstData == null ? SizedBox() : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - _ayatTangheemTypeMappedData.tangheemTypeName ?? "", + _ayatTangheemTypeMappedFirstData.tangheemTypeName ?? "", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: ColorConsts.primaryBlue, height: 1.5), ), SizedBox(height: 8), @@ -122,94 +149,127 @@ class _TangheemDetailScreenState extends State { child: RepaintBoundary( key: _globalKey, child: Material( - color: Colors.white, - child: ListView( - physics: NeverScrollableScrollPhysics(), - shrinkWrap: true, - padding: EdgeInsets.all(4), - children: [ - TextHighLightWidget( - text: _ayatTangheemTypeMappedData.reverseAyatNumber() ?? "", - valueColor: ColorConsts.primaryBlue, - highlights: _tangheemWords, - style: TextStyle( - fontFamily: "UthmanicHafs", - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - SizedBox(height: 16), - ListView.separated( - itemCount: _tangheemInsideTableValueList.length, - physics: NeverScrollableScrollPhysics(), - shrinkWrap: true, - separatorBuilder: (context, index) { - return Divider( - color: Colors.white, - height: 1, - thickness: 0, - ); - }, - itemBuilder: (context, index) { - return Row( - children: [ - Expanded( - child: Container( - height: 40, - padding: EdgeInsets.only(left: 4, right: 8), - alignment: Alignment.centerRight, - child: Text( - _tangheemInsideTableValueList[index].propertyText, - style: TextStyle(fontWeight: FontWeight.bold, color: ColorConsts.secondaryOrange), - ), - color: ColorConsts.secondaryWhite, + color: Colors.white, + child: ListView.builder( + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: ayatTangheemTypeMappedDataList.length > 5 ? 5 : ayatTangheemTypeMappedDataList.length, + itemBuilder: (context, index) { + var _ayatTangheemTypeMappedData = ayatTangheemTypeMappedDataList[index]; + List _tangheemInsideTableTrueList = []; + List _tangheemInsideTableValueList = []; + List _tangheemInsideTableEmptyList = []; + List _tangheemWords = []; + + _tangheemWords.add(_ayatTangheemTypeMappedData.highlightText ?? ""); + _tangheemInsideTableTrueList = + _ayatTangheemTypeMappedData?.property?.where((element) => (element.isInsideTable) && (element.propertyValue ?? "").isNotEmpty)?.toList() ?? []; + _tangheemInsideTableValueList = + _ayatTangheemTypeMappedData?.property?.where((element) => (!element.isInsideTable) && (element.propertyValue ?? "").isNotEmpty)?.toList() ?? []; + // _tangheemInsideTableEmptyList = _ayatTangheemTypeMappedData?.property?.where((element) => (!element.isInsideTable) && (element.propertyValue ?? "").isEmpty)?.toList() ?? []; + + return ListView( + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + padding: EdgeInsets.all(4), + children: [ + Row( + children: [ + Text( + " جمله ${_ayatTangheemTypeMappedData.tangheemTypeName} ${getArabicIndexWord(index)} ", + style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white, backgroundColor: ColorConsts.primaryBlue), ), - ), - SizedBox(width: 8), - Expanded( - child: Container( - height: 40, - padding: EdgeInsets.only(left: 4, right: 8), - alignment: Alignment.centerRight, - child: Text( - _tangheemInsideTableValueList[index].propertyValue, - style: TextStyle(color: ColorConsts.primaryBlack), - ), - color: ColorConsts.secondaryWhite, + Expanded( + child: Container(height: 2, color: ColorConsts.primaryBlue), ), - ) - ], - ); - }), - if (_tangheemInsideTableTrueList.isNotEmpty) - Container( - color: ColorConsts.primaryBlue, - margin: EdgeInsets.only(top: 8, bottom: 8), - padding: EdgeInsets.all(8), - child: Column( - children: [ - Text( - _ayatTangheemTypeMappedData.tangheemTypeName ?? "", - style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white), + ], ), SizedBox(height: 8), - tangheemPropertyView(_tangheemInsideTableTrueList) + TextHighLightWidget( + text: _ayatTangheemTypeMappedData.reverseAyatNumber() ?? "", + valueColor: ColorConsts.primaryBlue, + highlights: _tangheemWords, + highLightFontSize: fontSize, + style: TextStyle( + fontFamily: "UthmanicHafs", + fontSize: fontSize, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 16), + ListView.separated( + itemCount: _tangheemInsideTableValueList.length, + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + separatorBuilder: (context, index) { + return Divider( + color: Colors.white, + height: 1, + thickness: 0, + ); + }, + itemBuilder: (context, index) { + return Row( + children: [ + Expanded( + child: Container( + height: 40, + padding: EdgeInsets.only(left: 4, right: 8), + alignment: Alignment.centerRight, + child: Text( + _tangheemInsideTableValueList[index].propertyText, + style: TextStyle(fontWeight: FontWeight.bold, color: ColorConsts.secondaryOrange), + ), + color: ColorConsts.secondaryWhite, + ), + ), + SizedBox(width: 8), + Expanded( + child: Container( + height: 40, + padding: EdgeInsets.only(left: 4, right: 8), + alignment: Alignment.centerRight, + child: Text( + _tangheemInsideTableValueList[index].propertyValue, + style: TextStyle(color: ColorConsts.primaryBlack), + ), + color: ColorConsts.secondaryWhite, + ), + ) + ], + ); + }), + if (_tangheemInsideTableTrueList.isNotEmpty) + Container( + color: ColorConsts.primaryBlue, + margin: EdgeInsets.only(top: 8, bottom: 8), + padding: EdgeInsets.all(8), + child: Column( + children: [ + Text( + _ayatTangheemTypeMappedData.tangheemTypeName ?? "", + style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white), + ), + SizedBox(height: 8), + tangheemPropertyView(_tangheemInsideTableTrueList) + ], + ), + ), + tangheemPropertyView(_tangheemInsideTableEmptyList) ], - ), - ), - tangheemPropertyView(_tangheemInsideTableEmptyList) - ], - ), - ), + ); + })), ), ), ), SizedBox(height: 8), discussionView(_discussionModel?.data ?? []), + SizedBox(height: 16), + AyaRecordWidget() ], ), ), - AyaPlayerWidget(surahName: _ayatTangheemTypeMappedData.surahNameAr ?? "", globalKey: _globalKey, voiceNoteList: _ayatTangheemTypeMappedData?.voiceNote ?? []) + AyaPlayerWidget(surahName: _ayatTangheemTypeMappedFirstData?.surahNameAr ?? "", globalKey: _globalKey, voiceNoteList: voiceNoteList) ], ), ); @@ -308,60 +368,71 @@ class _TangheemDetailScreenState extends State { color: Colors.white, borderRadius: BorderRadius.circular(8), ), - child: ListView.separated( - padding: EdgeInsets.only(top: 4, bottom: 24), - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - itemCount: _discussionList.length, - separatorBuilder: (context, index) => SizedBox(height: 16), - itemBuilder: (context, index) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Row( - children: [ - SvgPicture.asset( - "assets/icons/chat_user.svg", - width: 60, - height: 60, - ), - SizedBox(width: 8), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - "تعليق على الآية ${widget.ayatTangheemTypeMappedData.ayatNumberInSurah}", - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16, color: ColorConsts.primaryBlue, height: 1.5), - ), - SizedBox(height: 4), - Text( - _discussionList[index].date.toFormattedDate(), - style: TextStyle(fontSize: 12, color: ColorConsts.textGrey, height: 1), - ), - ], - ) - ], - ), - SizedBox(height: 4), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Text( - // "عنوان التعليق", - // style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14, color: ColorConsts.primaryBlue, height: 1.5), - // ), - Text( - _discussionList[index].discussionText, - style: TextStyle(fontSize: 14, color: ColorConsts.textGrey, height: 1.4), - ), - ], - ) - ], - ); - }, - ), + child: _discussionList.length > 1 + ? ListView.separated( + padding: EdgeInsets.only(top: 4, bottom: 24), + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: _discussionList.length, + separatorBuilder: (context, index) => SizedBox(height: 16), + itemBuilder: (context, index) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + SvgPicture.asset( + "assets/icons/chat_user.svg", + width: 60, + height: 60, + ), + SizedBox(width: 8), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "تعليق على الآية ${_ayatTangheemTypeMappedFirstData.ayatNumberInSurah}", + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16, color: ColorConsts.primaryBlue, height: 1.5), + ), + SizedBox(height: 4), + Text( + _discussionList[index].date.toFormattedDate(), + style: TextStyle(fontSize: 12, color: ColorConsts.textGrey, height: 1), + ), + ], + ) + ], + ), + SizedBox(height: 4), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "تعليق من: " + _discussionList[index].userName, + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14, color: ColorConsts.primaryBlue, height: 1.5), + ), + Text( + _discussionList[index].discussionText, + style: TextStyle(fontSize: 14, color: ColorConsts.textGrey, height: 1.4), + ), + if ((_discussionList[index]?.adminResponse ?? "").isNotEmpty) SizedBox(height: 4), + if ((_discussionList[index]?.adminResponse ?? "").isNotEmpty) + Text( + "تعليق من: " + _discussionList[index].adminResponse, + style: TextStyle(fontSize: 14, color: ColorConsts.textGrey, height: 1.4), + ), + ], + ) + ], + ); + }, + ) + : Text( + "لا يوجد تعليقات", + style: TextStyle(fontSize: 12, color: ColorConsts.primaryBlue, height: 1.5), + ), ), Positioned( bottom: 0, @@ -369,11 +440,42 @@ class _TangheemDetailScreenState extends State { borderRadius: BorderRadius.circular(30), onTap: () async { if (!AppState().isUserLogin) { - await Navigator.pushNamed(context, LoginScreen.routeName); - if (!AppState().isUserLogin) { - Utils.showToast("يجب عليك تسجيل الدخول للتعليق"); - return; - } + Widget cancelButton = FlatButton( + child: Text("أرغب بالتسجيل"), + onPressed: () async { + Navigator.pop(context); + await Navigator.pushNamed(context, LoginScreen.routeName); + if (!AppState().isUserLogin) { + return; + } + }, + ); + Widget continueButton = FlatButton( + child: Text("استمرار كضيف"), + onPressed: () { + Navigator.pop(context); + return; + }, + ); + + // set up the AlertDialog + AlertDialog alert = AlertDialog( + content: Text("هذه الخاصية متاحه فقط للأعضاء المسجلين"), + actions: [ + cancelButton, + continueButton, + ], + ); + + // show the dialog + showDialog( + context: context, + builder: (BuildContext context) { + return alert; + }, + ); + + return; } showDialog( context: context, diff --git a/lib/ui/screens/tangheem_screen.dart b/lib/ui/screens/tangheem_screen.dart index d25da7d..e9f0607 100644 --- a/lib/ui/screens/tangheem_screen.dart +++ b/lib/ui/screens/tangheem_screen.dart @@ -7,6 +7,7 @@ import 'package:tangheem/models/aya_tangheem_type_mapped.dart'; import 'package:tangheem/models/surah_model.dart'; import 'package:tangheem/ui/misc/no_data_ui.dart'; import 'package:tangheem/ui/screens/tangheem_detail_screen.dart'; +import 'package:tangheem/widgets/aya_record_widget.dart'; import 'package:tangheem/widgets/text_highlight_widget.dart'; class TangheemScreen extends StatefulWidget { @@ -56,7 +57,8 @@ class _TangheemScreenState extends State { return _dataList == null ? SizedBox() : _dataList.isEmpty - ? NoDataUI() + ? //AyaRecordWidget() + NoDataUI() : ListView.separated( physics: BouncingScrollPhysics(), padding: EdgeInsets.all(16), @@ -67,7 +69,9 @@ class _TangheemScreenState extends State { itemBuilder: (context, index) { return InkWell( onTap: () { - Navigator.pushNamed(context, TangheemDetailScreen.routeName, arguments: _dataList[index]); + List list = []; + list = _ayatTangheemTypeMapped?.data?.where((element) => (element.ayahNo == _dataList[index].ayahNo) && (element.tangheemTypeId == _dataList[index].tangheemTypeId))?.toList() ?? []; + Navigator.pushNamed(context, TangheemDetailScreen.routeName, arguments:list); }, borderRadius: BorderRadius.circular(4), child: Container( @@ -89,7 +93,7 @@ class _TangheemScreenState extends State { style: TextStyle(fontSize: 12, color: ColorConsts.primaryBlue), ), Text( - " ${_dataList[index].ayahNo}", + " ${_dataList[index].ayatNumberInSurah}", style: TextStyle(fontSize: 14, color: ColorConsts.secondaryOrange), ), ], diff --git a/lib/widgets/aya_player_widget.dart b/lib/widgets/aya_player_widget.dart index b3da323..ff2e722 100644 --- a/lib/widgets/aya_player_widget.dart +++ b/lib/widgets/aya_player_widget.dart @@ -79,6 +79,26 @@ class _AyaPlayerWidgetState extends State { super.dispose(); } + int _tempIndex; + Uint8List temp; + + // made this dedicated function to remove avatar flashing + DecorationImage getDecodedImage(int index) { + if (_tempIndex == null || _tempIndex != index) { + _tempIndex = index; + String encodedImage = widget.voiceNoteList.elementAt(index).profilePicture; + if (encodedImage.contains("data:image/png;base64,")) { + encodedImage = encodedImage.replaceAll("data:image/png;base64,", ""); + } + temp = base64Decode(encodedImage); + } + + return DecorationImage( + fit: BoxFit.cover, + image: MemoryImage(temp), + ); + } + @override Widget build(BuildContext context) { return Container( @@ -106,7 +126,9 @@ class _AyaPlayerWidgetState extends State { ), itemBuilder: (context) { var _voiceList = widget.voiceNoteList; - return List.generate(_voiceList.length, (index) { + var length = _voiceList.length < 2 ? 0 : _voiceList.length; + + return List.generate(length, (index) { return PopupMenuItem( value: index, child: Text( @@ -120,21 +142,14 @@ class _AyaPlayerWidgetState extends State { StreamBuilder( stream: _player.currentIndexStream, builder: (context, snapshot) { - final state = snapshot.data; + int state = snapshot.data; if (state == null) return SizedBox(); return Container( width: 50.0, margin: EdgeInsets.only(left: 8, right: 8), height: 50.0, decoration: BoxDecoration( - image: widget.voiceNoteList.length < 1 - ? null - : DecorationImage( - fit: BoxFit.cover, - image: MemoryImage( - base64Decode(widget.voiceNoteList.elementAt(state).profilePicture), - ), - ), + image: widget.voiceNoteList.length < 1 ? null : getDecodedImage(state), borderRadius: BorderRadius.all( Radius.circular(30.0), ), @@ -153,7 +168,6 @@ class _AyaPlayerWidgetState extends State { ); }, ), - Expanded( child: Column( mainAxisSize: MainAxisSize.min, @@ -182,22 +196,17 @@ class _AyaPlayerWidgetState extends State { commonIconButton("assets/icons/download_aya.svg", () async { if (await _requestPermission()) { if (await _saveAya()) { - Utils.showToast("آية تم الحفظ بنجاح"); + Utils.showToast("تم حفظ الأية بنجاح"); } else { - Utils.showToast("فشل في حفظ الآية"); + Utils.showToast("خطأ في حفظ الآية"); } } else { - Utils.showToast("يجب أن تمنح الإذن لتنزيل الآية"); + Utils.showToast("يجب اعطاء الاذن لتنزيل الآية"); } }), commonIconButton("assets/icons/share_aya.svg", () { _shareAya(); }), - // commonIconButton("assets/icons/bookmark.svg", () {}), - // commonIconButton("assets/icons/audio_level.svg", () async { - // // var vol = await VolumeController.getVolume(); - // VolumeController.maxVolume(); - // }), ], ), SizedBox(height: 8), @@ -222,7 +231,7 @@ class _AyaPlayerWidgetState extends State { state ? _player.pause() : _isAudioHaveError - ? Utils.showToast("لم يتم تحميل ملف الصوت") + ? Utils.showToast("خطأ في تحميل ملف الصوت") : _player.play(); }); }, diff --git a/lib/widgets/aya_record_widget.dart b/lib/widgets/aya_record_widget.dart new file mode 100644 index 0000000..222c5e9 --- /dev/null +++ b/lib/widgets/aya_record_widget.dart @@ -0,0 +1,311 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:just_audio/just_audio.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:record_mp3/record_mp3.dart'; +import 'package:tangheem/classes/colors.dart'; +import 'package:tangheem/classes/utils.dart'; + +class AyaRecordWidget extends StatefulWidget { + AyaRecordWidget({Key key}) : super(key: key); + + @override + _AyaRecordWidgetState createState() => _AyaRecordWidgetState(); +} + +class _AyaRecordWidgetState extends State { + bool _isRecording = false; + AudioPlayer _audioPlayer; + bool _isAudioHaveError = false; + + @override + void initState() { + super.initState(); + _audioPlayer = AudioPlayer(); + } + + @override + void dispose() { + _audioPlayer.dispose(); + if (recordFilePath != null && File(recordFilePath).existsSync()) { + File(recordFilePath).deleteSync(); + } + super.dispose(); + } + + Future checkPermission() async { + if (!await Permission.microphone.isGranted) { + PermissionStatus status = await Permission.microphone.request(); + if (status != PermissionStatus.granted) { + return false; + } + } + return true; + } + + void startRecord() async { + bool hasPermission = await checkPermission(); + if (hasPermission) { + recordFilePath = await getFilePath(); + _isRecording = true; + RecordMp3.instance.start(recordFilePath, (type) { + _isRecording = false; + setState(() {}); + }); + } else { + Utils.showToast("يجب أن تمنح الإذن للتسجيل"); + } + setState(() {}); + } + + void stopRecord() async { + bool _isStop = RecordMp3.instance.stop(); + if (_isStop) { + _isRecording = false; + setState(() {}); + } + } + + String recordFilePath; + + void play() async { + if (recordFilePath != null && File(recordFilePath).existsSync()) { + try { + await _audioPlayer.setFilePath(recordFilePath); + _audioPlayer.play(); + } catch (ex) { + _isAudioHaveError = true; + } + } else { + Utils.showToast("يجب عليك التسجيل أولا"); + } + } + + Future getFilePath() async { + Directory storageDirectory = await getApplicationDocumentsDirectory(); + String sdPath = storageDirectory.path + "/record"; + var d = Directory(sdPath); + if (!d.existsSync()) { + d.createSync(recursive: true); + } + return sdPath + "/test.mp3"; + } + + @override + Widget build(BuildContext context) { + return Stack( + alignment: Alignment.bottomCenter, + children: [ + Container( + margin: EdgeInsets.only(top: 4, bottom: 20), + padding: EdgeInsets.only(bottom: 20), + width: double.infinity, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + ), + child: Column( + children: [ + Container( + height: 60, + width: double.infinity, + margin: EdgeInsets.only(bottom: 8), + alignment: Alignment.center, + decoration: BoxDecoration( + color: ColorConsts.primaryBlue, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(8), + topRight: Radius.circular(8), + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + SvgPicture.asset( + "assets/icons/mic_icon.svg", + width: 25, + height: 25, + ), + SizedBox(width: 12), + Text( + "سجل الآية بصوتك", + style: TextStyle(fontSize: 18, color: Colors.white), + ), + ], + ), + ), + InkWell( + onTap: () { + if (_isRecording) { + stopRecord(); + } else { + startRecord(); + } + }, + child: SvgPicture.asset( + _isRecording ? "assets/icons/recording_on.svg" : "assets/icons/recording_off.svg", + width: 60, + height: 60, + ), + ), + Container( + height: 50, + margin: EdgeInsets.all(16), + padding: EdgeInsets.only(left: 12, right: 12), + decoration: BoxDecoration( + color: ColorConsts.secondaryWhite, + borderRadius: BorderRadius.circular(30), + ), + child: Row( + children: [ + Expanded( + child: StreamBuilder( + stream: _audioPlayer.positionStream, + builder: (context, snapshot) { + final state = snapshot.data; + return SliderTheme( + data: SliderTheme.of(context).copyWith( + inactiveTrackColor: ColorConsts.sliderBackground, + activeTrackColor: ColorConsts.secondaryOrange, + trackHeight: 3.0, + thumbColor: ColorConsts.primaryBlack, + thumbShape: RoundSliderThumbShape(enabledThumbRadius: 6.0), + overlayColor: ColorConsts.primaryBlack.withAlpha(32), + overlayShape: RoundSliderOverlayShape(overlayRadius: 6.0), + ), + child: Directionality( + textDirection: TextDirection.ltr, + child: Slider( + value: (state?.inSeconds ?? 0) + 0.0, + min: 0, + max: (_audioPlayer?.duration?.inSeconds ?? 0) + 0.0, + onChanged: (value) {}, + ), + ), + ); + }, + ), + ), + SizedBox(width: 8), + StreamBuilder( + stream: _audioPlayer.positionStream, + builder: (context, snapshot) { + final state = snapshot.data; + return Text( + _durationTime(state) ?? "", + style: TextStyle(color: ColorConsts.textGrey1, height: 1.1, fontFamily: "Roboto", fontSize: 16), + ); + }, + ), + SizedBox(width: 8), + StreamBuilder( + stream: _audioPlayer.playerStateStream, + builder: (context, snapshot) { + final state = snapshot.data?.playing ?? false; + if (state) { + if (_audioPlayer?.duration?.inSeconds == _audioPlayer?.position?.inSeconds) { + _audioPlayer.pause(); + _audioPlayer.seek(Duration.zero); + } + } + return IconButton( + highlightColor: Colors.transparent, + splashColor: Colors.transparent, + constraints: BoxConstraints(), + padding: EdgeInsets.only(right: 0), + icon: SvgPicture.asset(state ? "assets/icons/pause.svg" : "assets/icons/play_aya.svg", height: 16, width: 16), + onPressed: () { + state + ? _audioPlayer.pause() + : _isAudioHaveError + ? Utils.showToast("خطأ في تحميل ملف الصوت") + : play(); + }); + }, + ), + ], + ), + ) + ], + ), + ), + Positioned( + bottom: 0, + child: InkWell( + borderRadius: BorderRadius.circular(30), + onTap: () async { + if (await _requestStoragePermission()) { + if (recordFilePath != null && File(recordFilePath).existsSync()) { + saveToPhoneStorage(recordFilePath); + } else { + Utils.showToast("يجب عليك تسجيل صوتك أولا"); + } + } else { + Utils.showToast("يجب أن تمنح الإذن لتنزيل التسجيل"); + } + }, + child: Container( + height: 40, + padding: EdgeInsets.only(left: 24, right: 24), + alignment: Alignment.centerRight, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30), + color: ColorConsts.gradientPink, + gradient: LinearGradient( + stops: [0.0, 0.5], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ColorConsts.gradientPink, ColorConsts.gradientOrange], + ), + ), + child: Text( + "تنزيل التسجيل", + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14, color: Colors.white, height: 1.5), + ), + ), + ), + ), + ], + ); + } + + Future _requestStoragePermission() async { + Map statuses = await [ + Permission.storage, + ].request(); + + return statuses[Permission.storage].isGranted; + } + + String _durationTime(Duration duration) { + if (duration == null) { + return "00:00"; + } + String twoDigits(int n) => n.toString().padLeft(2, "0"); + String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60)); + String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60)); + return "$twoDigitMinutes:$twoDigitSeconds"; + } + + void saveToPhoneStorage(String filePath) async { + File file = File(filePath); + + Directory storageDirectory = await getExternalStorageDirectory(); + + String storagePath = storageDirectory.path; + if (storagePath.contains("/Android/data")) { + storagePath = storagePath.substring(0, storagePath.indexOf("/Android/data")); + } + storagePath += "/tangheem/record/"; + var d = Directory(storagePath); + if (!d.existsSync()) { + d.createSync(recursive: true); + } + storagePath += "Tangheem${DateTime.now().millisecondsSinceEpoch}.mp3"; + await file.copy(storagePath); + Utils.showToast("تم التنزيل"); + } +} diff --git a/lib/widgets/text_highlight_widget.dart b/lib/widgets/text_highlight_widget.dart index ba98e80..932e4f6 100644 --- a/lib/widgets/text_highlight_widget.dart +++ b/lib/widgets/text_highlight_widget.dart @@ -11,6 +11,7 @@ class TextHighLightWidget extends StatelessWidget { final TextStyle style; final TextAlign textAlign; final Color highLightColor; + final double highLightFontSize; final Function(String) onTap; final Function(String) onAyaTap; @@ -23,6 +24,7 @@ class TextHighLightWidget extends StatelessWidget { this.highlights, this.highLightColor = ColorConsts.secondaryOrange, this.style = const TextStyle(), + this.highLightFontSize = 18, this.onTap, this.onAyaTap}); @@ -97,7 +99,7 @@ class TextHighLightWidget extends StatelessWidget { if (style.color == null) { return TextSpan( text: value, - style: style.copyWith(color: highLightColor, fontSize: 18), + style: style.copyWith(color: highLightColor, fontSize: highLightFontSize), recognizer: TapGestureRecognizer() ..onTap = () { if (onTap != null) { diff --git a/pubspec.yaml b/pubspec.yaml index cf12363..ac68590 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,6 +39,7 @@ dependencies: shared_preferences: ^2.0.5 url_launcher: ^6.0.3 flutter_slidable: ^0.6.0 + record_mp3: ^2.1.0 dev_dependencies: flutter_test: