Skip to content

Commit

Permalink
Merge pull request #1173 from mawaqit/revert-1167-revert-1120-feat/qu…
Browse files Browse the repository at this point in the history
…ran/download

Revert "Revert "Feat/quran/download"" [add again download quran]
  • Loading branch information
YassinNouh21 authored Jun 6, 2024
2 parents 9a0815f + 8a1465f commit 4467f18
Show file tree
Hide file tree
Showing 18 changed files with 1,133 additions and 140 deletions.
2 changes: 2 additions & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ linter:
rules:
- cancel_subscriptions
- require_trailing_commas
- close_sinks
- always_use_package_imports
16 changes: 14 additions & 2 deletions lib/l10n/intl_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -199,5 +199,17 @@
"update": "Update",
"automaticUpdate": "Notify update",
"automaticUpdateDescription": "Enable notify update to receive the latest features and improvements",
"later": "Later"
}
"later": "Later",
"downloadQuran": "Download Quran",
"quran": "Quran",
"askDownloadQuran": "Do you want to download the Quran?",
"download": "Download",
"downloadingQuran": "Downloading Quran",
"extractingQuran": "Extracting Quran",
"updatedQuran": "Quran updated",
"quranLatestVersion": "Quran is up to date",
"quranUpdatedVersion": "Quran updated version is: {version}",
"quranIsUpdated": "Quran is updated",
"quranDownloaded": "Quran downloaded",
"quranIsAlreadyDownloaded": "Quran is already downloaded"
}
13 changes: 13 additions & 0 deletions lib/src/const/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,16 @@ abstract class RandomHadithConstant {
static const String kHadithLanguage = "hadith_language";
static const String kLastHadithXMLFetchLanguage = "last_hadith_xml_language";
}

abstract class AnnouncementConstant {
static const String kBoxName = "announcement";
}

abstract class MosqueManagerConstant {
static const String kMosqueUUID = "mosqueUUID";
}

abstract class QuranConstant {
static const String kQuranVersionLink = "'https://mawaqit.github.io/mawaqit-announcements/public/quran/config.json'";
static const String kQuranLocalVersion = 'quran_local_version';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import 'dart:developer';
import 'dart:io';

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mawaqit/src/const/constants.dart';
import 'package:mawaqit/src/helpers/directory_helper.dart';
import 'package:mawaqit/src/module/shared_preference_module.dart';
import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';

class DownloadQuranLocalDataSource {
final Directory applicationSupportDirectory;
final SharedPreferences sharedPreference;

DownloadQuranLocalDataSource({required this.applicationSupportDirectory, required this.sharedPreference});

/// [saveSvgFiles] saves the zip file to the local storage
Future<void> saveSvgFiles(List<File> svgFiles) async {
final quranDirectory = Directory('${applicationSupportDirectory.path}/quran');
log('quran: DownloadQuranLocalDataSource: saveSvgFiles - quranDirectory: ${quranDirectory.path}');
await DirectoryHelper.createDirectoryIfNotExists(quranDirectory);
for (final svgFile in svgFiles) {
final fileName = svgFile.path.split('/').last;
final destinationPath = '${quranDirectory.path}/$fileName';
log('quran: DownloadQuranLocalDataSource: saveSvgFiles - destinationPath: $destinationPath || fileName: $fileName');
await svgFile.copy(destinationPath);
log('quran: DownloadQuranLocalDataSource: saveSvgFiles - copied ${svgFile.path} to $destinationPath');
}
}

/// [deleteZipFile] deletes the existing svg files
Future<void> deleteZipFile(String zipFileName) async {
final zipFilePath = '${applicationSupportDirectory.path}/quran_zip/$zipFileName';
_setQuranVersion(zipFileName);
final zipFile = File(zipFilePath);
if (await zipFile.exists()) {
log('quran: DownloadQuranLocalDataSource: deleteZipFile - deleting $zipFilePath');
await zipFile.delete();
}
}

/// [getQuranVersion] fetches the quran version
String getQuranVersion() {
final version = sharedPreference.getString(QuranConstant.kQuranLocalVersion) ?? '';
final checkSVGs = Directory('${applicationSupportDirectory.path}/quran');
if (checkSVGs.existsSync() && checkSVGs.listSync().isNotEmpty) {
log('quran: DownloadQuranLocalDataSource: getQuranVersion - checkSVGs: $version');
return version;
} else {
log('quran: DownloadQuranLocalDataSource: getQuranVersion - checkSVGs: $version');
return '';
}
}

/// [_setQuranVersion] sets the quran version
void _setQuranVersion(String version) {
sharedPreference.setString(QuranConstant.kQuranLocalVersion, version);
log('quran: DownloadQuranLocalDataSource: setQuranVersion - version: $version');
}
}

final downloadQuranLocalDataSourceProvider = FutureProvider<DownloadQuranLocalDataSource>(
(ref) async {
final savePath = await getApplicationSupportDirectory();
final sharedPref = await ref.read(sharedPreferenceModule.future);

return DownloadQuranLocalDataSource(
applicationSupportDirectory: savePath,
sharedPreference: sharedPref,
);
},
);
114 changes: 114 additions & 0 deletions lib/src/data/data_source/quran/download_quran_remote_data_source.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import 'dart:async';
import 'dart:developer';

import 'package:dio/dio.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mawaqit/src/const/constants.dart';
import 'package:mawaqit/src/domain/error/quran_exceptions.dart';
import 'package:mawaqit/src/helpers/directory_helper.dart';
import 'package:path_provider/path_provider.dart';

class DownloadQuranRemoteDataSource {
final Dio dio;
CancelToken? cancelToken;
final String applicationSupportDirectoryPath;

DownloadQuranRemoteDataSource({
required this.dio,
required this.applicationSupportDirectoryPath,
this.cancelToken,
});

/// [getRemoteQuranVersion] fetches the remote quran version
Future<String> getRemoteQuranVersion({
String url = "https://mawaqit.github.io/mawaqit-announcements/public/quran/config.json",
}) async {
try {
log('quran: DownloadQuranRemoteDataSource: getRemoteQuranVersion - start');
final version = await dio.get(url).then((value) {
return value.data['fileName'];
});
log('quran: DownloadQuranRemoteDataSource: getRemoteQuranVersion - $version');
return version as String;
} catch (e) {
throw FetchRemoteQuranVersionException(e.toString());
}
}

/// [downloadQuranWithProgress] downloads the quran zip file
Future<void> downloadQuranWithProgress({
required String versionName,
String? applicationDirectoryPath,
String url = "https://mawaqit.github.io/mawaqit-announcements/public/quran/",
Function(double)? onReceiveProgress,
}) async {
log('quran: DownloadQuranRemoteDataSource: downloadQuranWithProgress - start');
applicationDirectoryPath ??= applicationSupportDirectoryPath;

final filePath = '$applicationDirectoryPath/quran_zip/$versionName';
log('quran: DownloadQuranRemoteDataSource: downloadQuranWithProgress - filePath: $filePath');

try {
await dio.download(
'$url$versionName',
filePath,
onReceiveProgress: (received, total) {
final progress = (received / total) * 100;
// log('quran: DownloadQuranRemoteDataSource: downloadQuranWithProgress - progress: $progress');
onReceiveProgress?.call(progress);
},
cancelToken: cancelToken,
);
} on DioException catch (e) {
if (e.type == DioExceptionType.cancel) {
log('quran: DownloadQuranRemoteDataSource: downloadQuranWithProgress - download cancelled');
await DirectoryHelper.deleteFileIfExists(filePath);
await DirectoryHelper.deleteDirectories([
'$applicationDirectoryPath/quran_zip',
'$applicationDirectoryPath/quran',
]);
throw CancelDownloadException();
}
throw Exception('Error occurred while downloading quran: $e');
} catch (e) {
await DirectoryHelper.deleteFileIfExists(filePath);
await DirectoryHelper.deleteDirectories([
'$applicationDirectoryPath/quran_zip',
'$applicationDirectoryPath/quran',
]);
throw UnknownException(e.toString());
}
}

/// [cancelDownload] cancels the download
void cancelDownload() {
cancelToken?.cancel();
cancelToken = CancelToken();
log('quran: DownloadQuranRemoteDataSource: cancelDownload - download cancelled');
}
}

final downloadQuranRemoteDataSourceProvider = FutureProvider<DownloadQuranRemoteDataSource>(
(ref) async {
final savePath = await getApplicationSupportDirectory();
final cancelToken = CancelToken();
return DownloadQuranRemoteDataSource(
applicationSupportDirectoryPath: savePath.path,
cancelToken: cancelToken,
dio: ref.read(dioProvider),
);
},
);

final dioProvider = Provider(
(ref) => Dio(
BaseOptions(
baseUrl: kBaseUrl,
headers: {
'Api-Access-Token': kApiToken,
'accept': 'application/json',
'mawaqit-device': 'android-tv',
},
),
),
);
91 changes: 91 additions & 0 deletions lib/src/data/repository/quran/quran_download_impl.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mawaqit/src/data/data_source/quran/download_quran_remote_data_source.dart';
import 'package:mawaqit/src/helpers/directory_helper.dart';
import 'package:mawaqit/src/helpers/zip_extractor_helper.dart';

import 'package:mawaqit/src/data/data_source/quran/download_quran_local_data_source.dart';

import 'package:mawaqit/src/domain/repository/quran/quran_download_repository.dart';

class QuranDownloadRepositoryImpl implements QuranDownloadRepository {
final DownloadQuranLocalDataSource localDataSource;
final DownloadQuranRemoteDataSource remoteDataSource;

QuranDownloadRepositoryImpl({
required this.localDataSource,
required this.remoteDataSource,
});

/// [getLocalQuranVersion] fetches the local quran version
@override
Future<String?> getLocalQuranVersion() async {
final version = localDataSource.getQuranVersion();
return version;
}

/// [downloadQuran] downloads the quran zip file
@override
Future<void> downloadQuran({
required String version,
String? filePath,
required Function(double) onReceiveProgress,
}) async {
await remoteDataSource.downloadQuranWithProgress(
versionName: version,
onReceiveProgress: onReceiveProgress,
);
}

/// [extractQuran] extracts the quran zip file
@override
Future<void> extractQuran({
required String zipFilePath,
required String destinationPath,
required Function(double) onExtractProgress,
}) async {
await ZipFileExtractorHelper.extractZipFile(
zipFilePath: zipFilePath,
destinationDirPath: destinationPath,
changeProgress: (progress) {
onExtractProgress(progress);
},
);
}

/// [deleteOldQuran] deletes the old quran
@override
Future<void> deleteOldQuran({
String? path,
}) async {
final quranPath = path ?? localDataSource.applicationSupportDirectory.path;
final deletePath = '$quranPath/quran';
await DirectoryHelper.deleteExistingSvgFiles(path: deletePath);
}

/// [deleteZipFile] deletes the zip file
@override
Future<void> deleteZipFile(String zipFileName) async {
await localDataSource.deleteZipFile(zipFileName);
}

/// [getRemoteQuranVersion] fetches the remote quran version
@override
Future<String> getRemoteQuranVersion() {
return remoteDataSource.getRemoteQuranVersion();
}

/// [cancelDownload] cancels the download
@override
Future<void> cancelDownload() async {
remoteDataSource.cancelDownload();
}
}

final quranDownloadRepositoryProvider = FutureProvider<QuranDownloadRepository>((ref) async {
final localDataSource = await ref.read(downloadQuranLocalDataSourceProvider.future);
final remoteDataSource = await ref.read(downloadQuranRemoteDataSourceProvider.future);
return QuranDownloadRepositoryImpl(
localDataSource: localDataSource,
remoteDataSource: remoteDataSource,
);
});
71 changes: 71 additions & 0 deletions lib/src/domain/error/quran_exceptions.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// exceptions.dart

abstract class QuranException implements Exception {
final String message;
final String errorCode;

QuranException(this.message, this.errorCode);

@override
String toString() => 'Error ($errorCode): $message';
}

// DownloadQuranRemoteDataSource exceptions

class FetchRemoteQuranVersionException extends QuranException {
FetchRemoteQuranVersionException(String message)
: super('Error occurred while fetching remote Quran version: $message', 'FETCH_REMOTE_QURAN_VERSION_ERROR');
}

class DownloadQuranException extends QuranException {
DownloadQuranException(String message)
: super('Error occurred while downloading Quran: $message', 'DOWNLOAD_QURAN_ERROR');
}

class CancelDownloadException extends QuranException {
CancelDownloadException() : super('Download cancelled', 'CANCEL_DOWNLOAD_ERROR');
}

// DownloadQuranLocalDataSource exceptions

class SaveSvgFilesException extends QuranException {
SaveSvgFilesException(String message)
: super('Error occurred while saving SVG files: $message', 'SAVE_SVG_FILES_ERROR');
}

class DeleteExistingSvgFilesException extends QuranException {
DeleteExistingSvgFilesException(String message)
: super('Error occurred while deleting existing SVG files: $message', 'DELETE_EXISTING_SVG_FILES_ERROR');
}

class DeleteZipFileException extends QuranException {
DeleteZipFileException(String message)
: super('Error occurred while deleting zip file: $message', 'DELETE_ZIP_FILE_ERROR');
}

class ExtractZipFileException extends QuranException {
ExtractZipFileException(String message)
: super('Error occurred while extracting zip file: $message', 'EXTRACT_ZIP_FILE_ERROR');
}

class CreateDirectoryException extends QuranException {
CreateDirectoryException(String message)
: super('Error occurred while creating directory: $message', 'CREATE_DIRECTORY_ERROR');
}

class FileNotFOUNDException extends QuranException {
FileNotFOUNDException(String message) : super('File not found: $message', 'FILE_NOT_FOUND_ERROR');
}

class InvalidZipFileException extends QuranException {
InvalidZipFileException(String message) : super('Invalid zip file: $message', 'INVALID_ZIP_FILE_ERROR');
}

class ZipFileAlreadyExtractedException extends QuranException {
ZipFileAlreadyExtractedException(String message)
: super('Zip file already extracted: $message', 'ZIP_FILE_ALREADY_EXTRACTED_ERROR');
}

class UnknownException extends QuranException {
UnknownException(String message) : super('Unknown error: $message', 'UNKNOWN_ERROR');
}
Loading

0 comments on commit 4467f18

Please sign in to comment.