Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/904'
Browse files Browse the repository at this point in the history
  • Loading branch information
Salma committed Nov 21, 2023
2 parents e1d96a7 + 27932e5 commit 72a3181
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 215 deletions.
41 changes: 2 additions & 39 deletions example/lib/home_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import 'dart:io';
import 'dart:isolate';
import 'dart:ui';

import 'package:android_path_provider/android_path_provider.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/material.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
Expand Down Expand Up @@ -363,48 +362,12 @@ class _MyHomePageState extends State<MyHomePage> {

Future<String?> _getSavedDir() async {
String? externalStorageDirPath;

if (Platform.isAndroid) {
try {
externalStorageDirPath = await AndroidPathProvider.downloadsPath;
} catch (err, st) {
print('failed to get downloads path: $err, $st');

final directory = await getExternalStorageDirectory();
externalStorageDirPath = directory?.path;
}
} else if (Platform.isIOS) {
// var dir = (await _dirsOnIOS)[0]; // temporary
// var dir = (await _dirsOnIOS)[1]; // applicationSupport
// var dir = (await _dirsOnIOS)[2]; // library
var dir = (await _dirsOnIOS)[3]; // applicationDocuments
// var dir = (await _dirsOnIOS)[4]; // downloads

dir ??= await getApplicationDocumentsDirectory();
externalStorageDirPath = dir.absolute.path;
}
externalStorageDirPath =
(await getApplicationDocumentsDirectory()).absolute.path;

return externalStorageDirPath;
}

Future<List<Directory?>> get _dirsOnIOS async {
final temporary = await getTemporaryDirectory();
final applicationSupport = await getApplicationSupportDirectory();
final library = await getLibraryDirectory();
final applicationDocuments = await getApplicationDocumentsDirectory();
final downloads = await getDownloadsDirectory();

final dirs = [
temporary,
applicationSupport,
library,
applicationDocuments,
downloads
];

return dirs;
}

@override
Widget build(BuildContext context) {
return Scaffold(
Expand Down
4 changes: 0 additions & 4 deletions ios/Classes/DBManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@

-(NSArray *)loadDataFromDB:(NSString *)query withParameters:(NSArray *)parameters;

- (void)addLazilyColumnForTable:(const char *)table
column:(const char *)column
type:(const char *)type
defaultValue:(const char *)defaultValue;

-(void)executeQuery:(NSString *)query withParameters:(NSArray *)parameters;

Expand Down
71 changes: 0 additions & 71 deletions ios/Classes/DBManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -83,77 +83,6 @@ -(void)copyDatabaseIntoAppDirectory{
}
}

- (void)addLazilyColumnForTable:(const char *)table
column:(const char *)column
type:(const char *)type
defaultValue:(const char *)defaultValue {

sqlite3 *sqlite3Database;
NSString *databasePath = [self.appDirectory stringByAppendingPathComponent:self.databaseFilename];

if (debug) {
NSLog(@"databasePath: %@", databasePath);
}

int openDatabaseResult = sqlite3_open([databasePath UTF8String], &sqlite3Database);

if(openDatabaseResult != SQLITE_OK) {
if(debug) {
NSLog(@"error opening the database with error no.: %d", openDatabaseResult);
}

return;
}

if (debug) {
NSLog(@"open DB successfully");
}

sqlite3_stmt *compiledStatement;

const char *prefix = "select * from ";
char *select = (char *)malloc(strlen(prefix) + strlen(table) + 1);
sprintf(select, "%s%s", prefix, table);
int prepareStatementResult = sqlite3_prepare_v2(sqlite3Database, (const char *)select, -1, &compiledStatement, NULL);
if(prepareStatementResult == SQLITE_OK) {
int isExistTargetColumn = -1;
int totalColumns = sqlite3_column_count(compiledStatement);
for (int i = 0; i < totalColumns; i++) {
char *dbDataAsChars = (char *)sqlite3_column_text(compiledStatement, i);
dbDataAsChars = (char *)sqlite3_column_name(compiledStatement, i);
if (strcmp(dbDataAsChars, column) == 0) {
isExistTargetColumn = 0;
break;
}
}

if (isExistTargetColumn == -1) {
const char *component = "alter table";
const char *component1 = "add column";
const char *component2 = "default";
char *insert = (char *)malloc(strlen(component) + strlen(table) + strlen(component1) + strlen(column) + strlen(type) + strlen(column) + strlen(component2) + strlen(defaultValue) + 6 + 1);
sprintf(insert, "%s %s %s %s %s %s %s", component, table, component1, column, type, component2, defaultValue);
int execResult = sqlite3_exec(sqlite3Database, insert, NULL, NULL, NULL);
if (execResult != SQLITE_OK) {
if (debug) {
fprintf(stderr, "sqlite3_exec error :%s\r\n", sqlite3_errmsg(sqlite3Database));
}
}
} else {
if (debug) {
NSLog(@"DB table %s column %s already exists", table, column);
}
}
} else {
if (debug) {
NSLog(@"%s", sqlite3_errmsg(sqlite3Database));
}
}

sqlite3_finalize(compiledStatement);
sqlite3_close(sqlite3Database);
}


- (void)runQuery:(const char *)query withParameters:(NSArray *)parameters isQueryExecutable:(BOOL)queryExecutable {

Expand Down
122 changes: 21 additions & 101 deletions ios/Classes/FlutterDownloaderPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

#define KEY_URL @"url"
#define KEY_SAVED_DIR @"saved_dir"
#define KEY_SEARCH_DIR @"search_dir"

#define KEY_FILE_NAME @"file_name"
#define KEY_PROGRESS @"progress"
#define KEY_ID @"id"
Expand Down Expand Up @@ -58,8 +58,6 @@ @implementation FlutterDownloaderPlugin
static int _step = 10;
static NSMutableDictionary<NSString*, NSMutableDictionary*> *_runningTaskById = nil;

static NSSearchPathDirectory const kDefaultSearchPathDirectory = NSDocumentDirectory;

@synthesize databaseQueue;

- (instancetype)init:(NSObject<FlutterPluginRegistrar> *)registrar;
Expand Down Expand Up @@ -98,11 +96,6 @@ - (instancetype)init:(NSObject<FlutterPluginRegistrar> *)registrar;

_dbManager = [[DBManager alloc] initWithDatabaseFilePath:dbPath];

__typeof__(self) __weak weakSelf = self;
[self executeInDatabaseQueueForTask:^{
[weakSelf addDatabaseColumnForMakingFileCouldSaveInAnyDirectory];
}];

if (_runningTaskById == nil) {
_runningTaskById = [[NSMutableDictionary alloc] init];
}
Expand Down Expand Up @@ -315,13 +308,6 @@ - (void)executeInDatabaseQueueForTask:(void (^)(void))task {
});
}

+ (NSArray<NSNumber *> *)avaliableCommonDirectories {
return @[@(NSCachesDirectory),
@(NSApplicationSupportDirectory),
@(NSLibraryDirectory),
@(kDefaultSearchPathDirectory),
@(NSDownloadsDirectory)];;
}

- (BOOL)openDocumentWithURL:(NSURL*)url {
if (debug) {
Expand Down Expand Up @@ -382,31 +368,10 @@ - (NSURL*)fileUrlOf:(NSString*)taskId taskInfo:(NSDictionary*)taskInfo downloadT
return [self fileUrlFromDict:mutableTaskInfo];
}

- (NSString*)absoluteSavedDirPathWithShortSavedDir:(NSString*)shortSavedDir searchPathDirectory:(NSSearchPathDirectory)searchPathDirectory {
return [[NSSearchPathForDirectoriesInDomains(searchPathDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:shortSavedDir];
}

- (NSString *)sanitizeFilename:(nullable NSString *)filename {
// Define a list of allowed characters for filenames
NSMutableCharacterSet *allowedCharacters = [[NSMutableCharacterSet alloc] init];

// Allow alphabetical characters (lowercase and uppercase)
[allowedCharacters formUnionWithCharacterSet:[NSCharacterSet letterCharacterSet]];

// Allow digits
[allowedCharacters addCharactersInRange:NSMakeRange('0', 10)]; // ASCII digits

// Allow additional characters: -_.()
[allowedCharacters addCharactersInString:@"-_.()"];
NSCharacterSet *allowedCharacters = [[NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.() "] invertedSet];

// Allow empty spaces
[allowedCharacters addCharactersInString:@" "];

// Remove the backslash (if you want to disallow it)
[allowedCharacters removeCharactersInString:@"\\"];

// Now, you have a character set that allows the specified characters
NSCharacterSet *finalCharacterSet = [allowedCharacters copy];
if (filename == nil || [filename isEqual:[NSNull null]] || [filename isEqualToString:@""]) {
NSString *defaultFilename = @"default_filename";
return defaultFilename;
Expand Down Expand Up @@ -439,39 +404,30 @@ - (NSString *)sanitizeFilename:(nullable NSString *)filename {
}


- (NSString*)absoluteSavedDirPath:(NSString*)savedDir {
return [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:savedDir];
}

- (NSArray *)shortenSavedDirPath:(NSString*)absolutePath {
- (NSString*)shortenSavedDirPath:(NSString*)absolutePath {
if (debug) {
NSLog(@"Absolute savedDir path: %@", absolutePath);
}

for (NSNumber *element in self.class.avaliableCommonDirectories) {
NSString *shortSvedDirPath = [self shortenSavedDirPath:absolutePath searchPathDirectory:element.unsignedIntegerValue];
if (shortSvedDirPath) {
return @[shortSvedDirPath, element];
}
}

return @[@"", @(kDefaultSearchPathDirectory)];
}

- (NSString*)shortenSavedDirPath:(NSString*)absolutePath searchPathDirectory:(NSSearchPathDirectory)searchPathDirectory {
if (absolutePath) {
NSString *searchDirPath = [NSSearchPathForDirectoriesInDomains(searchPathDirectory, NSUserDomainMask, YES) firstObject];
if ([absolutePath isEqualToString:searchDirPath]) {
NSString* documentDirPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
if ([absolutePath isEqualToString:documentDirPath]) {
return @"";
}
NSRange foundRank = [absolutePath rangeOfString:searchDirPath];
NSRange foundRank = [absolutePath rangeOfString:documentDirPath];
if (foundRank.length > 0) {
// we increase the location of range by one because we want to remove the file separator as well.
NSString *shortenSavedDirPath = [absolutePath substringWithRange:NSMakeRange(foundRank.length + 1, absolutePath.length - searchDirPath.length - 1)];
NSString *shortenSavedDirPath = [absolutePath substringWithRange:NSMakeRange(foundRank.length + 1, absolutePath.length - documentDirPath.length - 1)];
return shortenSavedDirPath != nil ? shortenSavedDirPath : @"";
}
}

return nil;
}

return absolutePath;
}

- (long long)currentTimeInMilliseconds
{
Expand All @@ -480,27 +436,7 @@ - (long long)currentTimeInMilliseconds

# pragma mark - Database Accessing

/// Before version 1.11.1, FlutterDownloader only allows file to be saved in [NSDocumentDirectory]. This limits the freedom of development.
///
/// This function serves two purposes:
///
/// 1. Add a database column `search_dir` for determining common root directory such as the flowing directories
///
/// - NSCachesDirectory
/// - NSApplicationSupportDirectory
/// - NSLibraryDirectory
/// - NSDocumentDirectory
/// - NSDownloadsDirectory
///
/// Definition of common root directory refers to [path_provider](https://github.com/flutter/packages/blob/main/packages/path_provider/path_provider/lib/path_provider.dart).
///
/// 2. Resolve previous compatibility issue
- (void)addDatabaseColumnForMakingFileCouldSaveInAnyDirectory {
[_dbManager addLazilyColumnForTable:"task"
column:KEY_SEARCH_DIR.UTF8String
type:"integer"
defaultValue:[NSString stringWithFormat:@"%lu", kDefaultSearchPathDirectory].UTF8String]; // kDefaultSearchPathDirectory is [NSDocumentDirectory](9), this is compatible with previous FlutterDownloader versions.
}

- (NSString*) escape:(NSString*) origin revert:(BOOL)revert
{
if ( origin == (NSString *)[NSNull null] )
Expand All @@ -519,18 +455,16 @@ - (void)addNewTask:(NSString *)taskId
progress:(int)progress
filename:(NSString *)filename
savedDir:(NSString *)savedDir
searchDir:(NSSearchPathDirectory)searchDir
headers:(NSString *)headers
resumable:(BOOL)resumable
showNotification:(BOOL)showNotification
openFileFromNotification:(BOOL)openFileFromNotification {

headers = [self escape:headers revert:NO];

NSString *query = @"INSERT INTO task (task_id, url, status, progress, file_name, saved_dir, search_dir, headers, resumable, show_notification, open_file_from_notification, time_created) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
NSNumber *searchDirValue = @(searchDir);
NSString *query = @"INSERT INTO task (task_id, url, status, progress, file_name, saved_dir, headers, resumable, show_notification, open_file_from_notification, time_created) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
NSString *sanitizedFileName = [self sanitizeFilename:filename];
NSArray *values = @[taskId, url, @(status), @(progress), sanitizedFileName, savedDir, searchDirValue, headers, @(resumable ? 1:0), @(showNotification ? 1 : 0), @(openFileFromNotification ? 1: 0), @([self currentTimeInMilliseconds])];
NSArray *values = @[taskId, url, @(status), @(progress), sanitizedFileName, savedDir, headers, @(resumable ? 1:0), @(showNotification ? 1 : 0), @(openFileFromNotification ? 1: 0), @([self currentTimeInMilliseconds])];

[_dbManager executeQuery:query withParameters:values];

Expand Down Expand Up @@ -723,16 +657,9 @@ - (NSDictionary*) taskDictFromRecordArray:(NSArray*)record
int progress = [[record objectAtIndex:[_dbManager.arrColumnNames indexOfObject:@"progress"]] intValue];
NSString *url = [record objectAtIndex:[_dbManager.arrColumnNames indexOfObject:@"url"]];
NSString *filename = [record objectAtIndex:[_dbManager.arrColumnNames indexOfObject:@"file_name"]];
NSString *shortSavedDir = [record objectAtIndex:[_dbManager.arrColumnNames indexOfObject:@"saved_dir"]];

NSString *searchDirStr = [record objectAtIndex:[_dbManager.arrColumnNames indexOfObject:KEY_SEARCH_DIR]];
int searchDir = [searchDirStr intValue];
NSNumber *searchDirNum = [NSNumber numberWithInt:searchDir];

NSString *savedDir = [self absoluteSavedDirPathWithShortSavedDir:shortSavedDir searchPathDirectory:searchDir];

NSString *savedDir = [self absoluteSavedDirPath:[record objectAtIndex:[_dbManager.arrColumnNames indexOfObject:@"saved_dir"]]];
NSString *headers = @"";
// in certain cases, headers column might not be available and will cause NSRangeException
// in certain cases, headers column might not be available and will cause NSRangeException
@try {
NSString *rawHeaders = [record objectAtIndex:[_dbManager.arrColumnNames indexOfObject:@"headers"]];
headers = [self escape:rawHeaders revert:true];
Expand All @@ -743,7 +670,7 @@ - (NSDictionary*) taskDictFromRecordArray:(NSArray*)record
int showNotification = [[record objectAtIndex:[_dbManager.arrColumnNames indexOfObject:@"show_notification"]] intValue];
int openFileFromNotification = [[record objectAtIndex:[_dbManager.arrColumnNames indexOfObject:@"open_file_from_notification"]] intValue];
long long timeCreated = [[record objectAtIndex:[_dbManager.arrColumnNames indexOfObject:@"time_created"]] longLongValue];
return [NSDictionary dictionaryWithObjectsAndKeys:taskId, KEY_TASK_ID, @(status), KEY_STATUS, @(progress), KEY_PROGRESS, url, KEY_URL, filename, KEY_FILE_NAME, headers, KEY_HEADERS, savedDir, KEY_SAVED_DIR, searchDirNum, KEY_SEARCH_DIR, [NSNumber numberWithBool:(resumable == 1)], KEY_RESUMABLE, [NSNumber numberWithBool:(showNotification == 1)], KEY_SHOW_NOTIFICATION, [NSNumber numberWithBool:(openFileFromNotification == 1)], KEY_OPEN_FILE_FROM_NOTIFICATION, @(timeCreated), KEY_TIME_CREATED, nil];
return [NSDictionary dictionaryWithObjectsAndKeys:taskId, KEY_TASK_ID, @(status), KEY_STATUS, @(progress), KEY_PROGRESS, url, KEY_URL, filename, KEY_FILE_NAME, headers, KEY_HEADERS, savedDir, KEY_SAVED_DIR, [NSNumber numberWithBool:(resumable == 1)], KEY_RESUMABLE, [NSNumber numberWithBool:(showNotification == 1)], KEY_SHOW_NOTIFICATION, [NSNumber numberWithBool:(openFileFromNotification == 1)], KEY_OPEN_FILE_FROM_NOTIFICATION, @(timeCreated), KEY_TIME_CREATED, nil];
} @catch(NSException *exception) {
NSLog(@"invalid task data: %@", exception);
return [NSDictionary dictionary];
Expand Down Expand Up @@ -789,14 +716,8 @@ - (void) unqueueStatusEvents {

- (void)enqueueMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
NSString *urlString = call.arguments[KEY_URL];

NSString *savedDirFromSource = call.arguments[KEY_SAVED_DIR];
NSArray *shortSavedDirArgs = [self shortenSavedDirPath:savedDirFromSource];
NSString *shortSavedDir = shortSavedDirArgs[0];
NSNumber *searchDirNum = shortSavedDirArgs[1];
NSSearchPathDirectory searchDir = searchDirNum.unsignedIntegerValue;
NSString *savedDir = [self absoluteSavedDirPathWithShortSavedDir:shortSavedDir searchPathDirectory:searchDir];

NSString *savedDir = call.arguments[KEY_SAVED_DIR];
NSString *shortSavedDir = [self shortenSavedDirPath:savedDir];
NSString *fileName = call.arguments[KEY_FILE_NAME];
NSString *headers = call.arguments[KEY_HEADERS];
NSNumber *showNotification = call.arguments[KEY_SHOW_NOTIFICATION];
Expand All @@ -810,7 +731,6 @@ - (void)enqueueMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result
urlString, KEY_URL,
fileName, KEY_FILE_NAME,
savedDir, KEY_SAVED_DIR,
searchDirNum, KEY_SEARCH_DIR,
headers, KEY_HEADERS,
showNotification, KEY_SHOW_NOTIFICATION,
openFileFromNotification, KEY_OPEN_FILE_FROM_NOTIFICATION,
Expand All @@ -822,7 +742,7 @@ - (void)enqueueMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result
__typeof__(self) __weak weakSelf = self;

[self executeInDatabaseQueueForTask:^{
[weakSelf addNewTask:taskId url:urlString status:STATUS_ENQUEUED progress:0 filename:fileName savedDir:shortSavedDir searchDir:searchDir headers:headers resumable:NO showNotification: [showNotification boolValue] openFileFromNotification: [openFileFromNotification boolValue]];
[weakSelf addNewTask:taskId url:urlString status:STATUS_ENQUEUED progress:0 filename:fileName savedDir:shortSavedDir headers:headers resumable:NO showNotification: [showNotification boolValue] openFileFromNotification: [openFileFromNotification boolValue]];
}];
result(taskId);
[self sendUpdateProgressForTaskId:taskId inStatus:@(STATUS_ENQUEUED) andProgress:@0];
Expand Down

0 comments on commit 72a3181

Please sign in to comment.