tangheem audio download feature added. & home icon added

development
Sikander Saleem 4 years ago
parent 1737d2541e
commit 63779374fa

@ -5,44 +5,42 @@
In most cases you can leave this as-is, but you if you want to provide In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. --> FlutterApplication and put your custom class here. -->
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
<application <application
android:icon="@mipmap/ic_launcher"
android:label="Tangheem" android:label="Tangheem"
android:roundIcon="@mipmap/ic_launcher_round"
android:usesCleartextTraffic="true"
android:requestLegacyExternalStorage="true" android:requestLegacyExternalStorage="true"
android:icon="@mipmap/ic_launcher"> android:roundIcon="@mipmap/ic_launcher_round"
android:usesCleartextTraffic="true">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as <!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. --> to determine the Window background behind the Flutter UI. -->
<meta-data <meta-data
android:name="io.flutter.embedding.android.NormalTheme" android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" android:resource="@style/NormalTheme" />
/>
<!-- Displays an Android View that continues showing the launch screen <!-- Displays an Android View that continues showing the launch screen
Drawable until Flutter paints its first frame, then this splash Drawable until Flutter paints its first frame, then this splash
screen fades out. A splash screen is useful to avoid any visual screen fades out. A splash screen is useful to avoid any visual
gap between the end of Android's launch screen and the painting of gap between the end of Android's launch screen and the painting of
Flutter's first frame. --> Flutter's first frame. -->
<meta-data <meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable" android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background" android:resource="@drawable/launch_background" />
/>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<!-- Don't delete the meta-data below. <!-- Don't delete the meta-data below.

@ -84,6 +84,12 @@ class TangheemUserApiClient {
return await ApiClient().postJsonForObject((json) => AyatTangheemTypeMapped.fromJson(json), url, postParams); return await ApiClient().postJsonForObject((json) => AyatTangheemTypeMapped.fromJson(json), url, postParams);
} }
Future<AyatTangheemTypeMapped> getAyaTangheemTypeMappedFor(int surahNo, String numberInSurahs, String tangheemTypeId) async {
String url = "${ApiConsts.tangheemUsers}AyatTangheemTypeMapped_Get";
var postParams = {"surahNo": surahNo, "numberInSurahs": numberInSurahs, "tangheemTypeId": tangheemTypeId};
return await ApiClient().postJsonForObject((json) => AyatTangheemTypeMapped.fromJson(json), url, postParams);
}
Future<AyatTangheemTypeMapped> ayahBaseTextGet(String tangheemTypeName, String ayaText, int itemsPerPage, int currentPageNo) async { Future<AyatTangheemTypeMapped> ayahBaseTextGet(String tangheemTypeName, String ayaText, int itemsPerPage, int currentPageNo) async {
String url = "${ApiConsts.tangheemUsers}AyahBaseText_Get"; String url = "${ApiConsts.tangheemUsers}AyahBaseText_Get";
var postParams = {}; var postParams = {};
@ -139,9 +145,7 @@ class TangheemUserApiClient {
Future<DiscussionModel> getDiscussionByTangheemID(int pageNumber, String tangheemID) async { Future<DiscussionModel> getDiscussionByTangheemID(int pageNumber, String tangheemID) async {
String url = "${ApiConsts.tangheemUsers}Discussion_Get"; String url = "${ApiConsts.tangheemUsers}Discussion_Get";
var postParams = {"itemsPerPage": 5, "currentPageNo": pageNumber, "ayaTangheemTypeId": tangheemID}; var postParams = {"itemsPerPage": 5, "statusId": 2, "ayaTangheemTypeId": tangheemID};
postParams["itemsPerPage"] = null;
postParams["currentPageNo"] = null;
return await ApiClient().postJsonForObject((json) => DiscussionModel.fromJson(json), url, postParams); return await ApiClient().postJsonForObject((json) => DiscussionModel.fromJson(json), url, postParams);
} }

@ -15,6 +15,7 @@ import 'package:tangheem/models/quick_links_model.dart';
import 'package:tangheem/ui/dialogs/change_password_dialog.dart'; import 'package:tangheem/ui/dialogs/change_password_dialog.dart';
import 'package:tangheem/ui/screens/bookmark_screen.dart'; import 'package:tangheem/ui/screens/bookmark_screen.dart';
import 'package:tangheem/ui/screens/content_info_screen.dart'; import 'package:tangheem/ui/screens/content_info_screen.dart';
import 'package:tangheem/ui/screens/home_screen.dart';
import 'package:tangheem/ui/screens/login_screen.dart'; import 'package:tangheem/ui/screens/login_screen.dart';
import 'package:tangheem/ui/screens/pdf_viewer_screen.dart'; import 'package:tangheem/ui/screens/pdf_viewer_screen.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
@ -134,6 +135,14 @@ class _CommonAppbarState extends State<CommonAppbar> {
} }
}, },
), ),
if (!widget.showDrawer)
IconButton(
icon: Icon(widget.showDrawer ? Icons.home : Icons.home, color: ColorConsts.textGrey),
padding: EdgeInsets.only(left: 16),
onPressed: () {
Navigator.popUntil(context, ModalRoute.withName(HomeScreen.routeName));
},
),
Expanded(child: SizedBox()), Expanded(child: SizedBox()),
Hero( Hero(
tag: "logo", tag: "logo",

@ -86,6 +86,19 @@ class _TangheemScreenState extends State<TangheemScreen> {
setState(() {}); setState(() {});
} }
void getTangheemDataForTangheem(int surahNo, String numberInSurahs, String tangheemTypeId, String ayaTangheemTypeId) async {
Utils.showLoading(context);
try {
AyatTangheemTypeMapped _ayatTangheemTypeMapped = await TangheemUserApiClient().getAyaTangheemTypeMappedFor(surahNo, numberInSurahs, tangheemTypeId);
TangheemDetailParams tangheem = TangheemDetailParams(selectedTangheemTypeId: ayaTangheemTypeId, ayatTangheemTypeMappedDataList: _ayatTangheemTypeMapped.data ?? []);
Utils.hideLoading(context);
Navigator.pushNamed(context, TangheemDetailScreen.routeName, arguments: tangheem);
} catch (ex) {
if (mounted) Utils.handleException(ex, null);
Utils.hideLoading(context);
}
}
@override @override
void dispose() { void dispose() {
super.dispose(); super.dispose();
@ -108,13 +121,18 @@ class _TangheemScreenState extends State<TangheemScreen> {
itemBuilder: (context, index) { itemBuilder: (context, index) {
return InkWell( return InkWell(
onTap: () { onTap: () {
List<AyatTangheemTypeMappedData> list = <AyatTangheemTypeMappedData>[] + _dataList; if (widget.tangheemQuery == null) {
var removedData = _dataList[index]; List<AyatTangheemTypeMappedData> list = <AyatTangheemTypeMappedData>[] + _dataList;
list.remove(removedData); var removedData = _dataList[index];
list.insert(0, removedData); list.remove(removedData);
list = list?.where((element) => (element.ayahNos.contains(removedData.ayahNos)) && (element.tangheemTypeId == removedData.tangheemTypeId))?.toList() ?? []; list.insert(0, removedData);
TangheemDetailParams tangheem = TangheemDetailParams(selectedTangheemTypeId: removedData.ayaTangheemTypeId, ayatTangheemTypeMappedDataList: list); list = list?.where((element) => (element.ayahNos.contains(removedData.ayahNos)) && (element.tangheemTypeId == removedData.tangheemTypeId))?.toList() ?? [];
Navigator.pushNamed(context, TangheemDetailScreen.routeName, arguments: tangheem); TangheemDetailParams tangheem = TangheemDetailParams(selectedTangheemTypeId: removedData.ayaTangheemTypeId, ayatTangheemTypeMappedDataList: list);
Navigator.pushNamed(context, TangheemDetailScreen.routeName, arguments: tangheem);
} else {
var removedData = _dataList[index];
getTangheemDataForTangheem(removedData.surahNo, removedData.ayatNumberInSurahs, removedData.tangheemTypeId, removedData.ayaTangheemTypeId);
}
}, },
borderRadius: BorderRadius.circular(4), borderRadius: BorderRadius.circular(4),
child: Container( child: Container(

@ -1,14 +1,14 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'dart:math' as math; import 'dart:math' as math;
import 'dart:typed_data'; import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart'; import 'package:http/http.dart' as http;
import 'package:just_audio/just_audio.dart'; import 'package:just_audio/just_audio.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:tangheem/classes/colors.dart'; import 'package:tangheem/classes/colors.dart';
import 'package:tangheem/classes/consts.dart'; import 'package:tangheem/classes/consts.dart';
@ -196,17 +196,14 @@ class _AyaPlayerWidgetState extends State<AyaPlayerWidget> {
], ],
), ),
), ),
commonIconButton("assets/icons/download_aya.svg", () async { if (widget.voiceNoteList?.isNotEmpty ?? false)
if (await _requestPermission()) { commonIconButton("assets/icons/download_aya.svg", () async {
if (await _saveAya()) { if (await _requestPermission()) {
Utils.showToast("تم حفظ الآية بنجاح"); saveToPhoneStorage(widget.voiceNoteList[_player.currentIndex].exposeFilePath, widget.voiceNoteList[_player.currentIndex].userName ?? "");
} else { } else {
Utils.showToast("خطأ في حفظ الآية"); Utils.showToast("يجب اعطاء الاذن لتنزيل الآية");
} }
} else { }),
Utils.showToast("يجب اعطاء الاذن لتنزيل الآية");
}
}),
], ],
), ),
SizedBox(height: 8), SizedBox(height: 8),
@ -311,27 +308,82 @@ class _AyaPlayerWidgetState extends State<AyaPlayerWidget> {
} }
Future<bool> _requestPermission() async { Future<bool> _requestPermission() async {
Map<Permission, PermissionStatus> statuses = await [ Map<Permission, PermissionStatus> statuses = await [Permission.storage].request();
Permission.storage,
].request();
return statuses[Permission.storage].isGranted; return statuses[Permission.storage].isGranted;
} }
Future<bool> _saveAya() async { void saveToPhoneStorage(String filePath, String name) async {
Directory storageDirectory;
if (Platform.isAndroid) {
storageDirectory = await getExternalStorageDirectory();
} else if (Platform.isIOS) {
storageDirectory = await getApplicationDocumentsDirectory();
} else {
return;
}
String storagePath = storageDirectory.path;
if (storagePath.contains("/Android/data")) {
storagePath = storagePath.substring(0, storagePath.indexOf("/Android/data"));
}
storagePath += "/Download/Tangheem/";
var d = Directory(storagePath);
if (!d.existsSync()) {
d.createSync(recursive: true);
}
storagePath += "Tangheem-$name-${DateTime.now().millisecondsSinceEpoch}${filePath.substring(filePath.lastIndexOf('.'), filePath.length)}";
Utils.showLoading(context); Utils.showLoading(context);
try { downloadFile(ApiConsts.baseUrl + filePath, storagePath, onResponse: (isSuccess) {
RenderRepaintBoundary boundary = widget.globalKey.currentContext.findRenderObject();
ui.Image image = await boundary.toImage(pixelRatio: 3.0);
ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png);
final result = await ImageGallerySaver.saveImage(byteData.buffer.asUint8List(), quality: 100);
Utils.hideLoading(context); Utils.hideLoading(context);
return result["isSuccess"]; if (isSuccess) {
} catch (ex) { Utils.showToast("تم حفظ الآية بنجاح");
Future.delayed(Duration(seconds: 1), () { } else {
Utils.hideLoading(context); Utils.showToast("خطأ في حفظ الآية");
}); }
return false; });
} }
void downloadFile(String url, String path, {Function(bool) onResponse}) {
var httpClient = http.Client();
var request = http.Request('GET', Uri.parse(url));
var response = httpClient.send(request);
String dir = path;
List<List<int>> chunks = [];
int downloaded = 0;
int lastVal;
response.asStream().listen((http.StreamedResponse r) {
r.stream.listen((List<int> chunk) {
debugPrint('downloadPercentage: ${downloaded / r.contentLength * 100}');
lastVal = (downloaded / r.contentLength * 100).toInt();
chunks.add(chunk);
downloaded += chunk.length;
}, onDone: () async {
debugPrint('downloadPercentage: ${downloaded / r.contentLength * 100}');
if (lastVal == 0) {
onResponse(false);
return;
}
// Save the file
File file = new File('$dir');
final Uint8List bytes = Uint8List(r.contentLength);
int offset = 0;
for (List<int> chunk in chunks) {
bytes.setRange(offset, offset + chunk.length, chunk);
offset += chunk.length;
}
await file.writeAsBytes(bytes);
onResponse(true);
return;
}, onError: (ex) {
debugPrint("onError:$ex");
}, cancelOnError: true);
}).onError((ex) {
debugPrint("onError:$ex");
onResponse(false);
});
} }
} }

@ -279,9 +279,7 @@ class _AyaRecordWidgetState extends State<AyaRecordWidget> {
} }
Future<bool> _requestStoragePermission() async { Future<bool> _requestStoragePermission() async {
Map<Permission, PermissionStatus> statuses = await [ Map<Permission, PermissionStatus> statuses = await [Permission.storage].request();
Permission.storage,
].request();
return statuses[Permission.storage].isGranted; return statuses[Permission.storage].isGranted;
} }
@ -310,12 +308,13 @@ class _AyaRecordWidgetState extends State<AyaRecordWidget> {
if (storagePath.contains("/Android/data")) { if (storagePath.contains("/Android/data")) {
storagePath = storagePath.substring(0, storagePath.indexOf("/Android/data")); storagePath = storagePath.substring(0, storagePath.indexOf("/Android/data"));
} }
storagePath += "/tangheem/record/"; storagePath += "/Download/Tangheem/";
var d = Directory(storagePath); var d = Directory(storagePath);
if (!d.existsSync()) { if (!d.existsSync()) {
d.createSync(recursive: true); d.createSync(recursive: true);
} }
storagePath += "Tangheem${DateTime.now().millisecondsSinceEpoch}.mp3"; storagePath += "Tangheem${DateTime.now().millisecondsSinceEpoch}.mp3";
print("storagePath:$storagePath");
await file.copy(storagePath); await file.copy(storagePath);
Utils.showToast("تم التنزيل"); Utils.showToast("تم التنزيل");
} }

Loading…
Cancel
Save