-
The main model part functionalities provided by the fs_manager and fs_operation methods.
The provided fs_manager API is used by the GUI elements to get data the for UI component: list of available storages, list of files and subfolder for specified folder; make copy, move, delete operations on files, and folders.
To get a list of available storages, use the fs_manager_get_storage_list() function.
int
fs_manager_get_storage_list(fs_manager *manager, Eina_List **storage_list)
{
RETVM_IF(!manager, RESULT_TYPE_INVALID_ARG, "File manager is NULL");
if (manager->is_locked)
{
ERR("File manager is busy");
return RESULT_TYPE_BUSY;
}
bool is_supported = false;
int res = _fs_manager_is_mmc_supported(&is_supported);
if (res != RESULT_TYPE_OK)
{
return res;
}
if (is_supported)
{
storage_info *const pNode_internal = calloc(1, sizeof(node_info));
pNode_internal->root_name = strdup(FM_MEMORY_LABEL);
pNode_internal->root_path = strdup(FM_MEMORY_FOLDER);
pNode_internal->type = STORAGE_TYPE_MMC;
*storage_list = eina_list_append(*storage_list, pNode_internal);
}
storage_info *const pNode_device = calloc(1, sizeof(node_info));
pNode_device->root_name = strdup(FM_PHONE_LABEL);
pNode_device->root_path = strdup(FM_PHONE_FOLDER);
pNode_device->type = STORAGE_TYPE_PHONE;
*storage_list = eina_list_append(*storage_list, pNode_device);
*storage_list = eina_list_sort(*storage_list, eina_list_count(*storage_list), _fs_manager_sort_by_name_cb);
return RESULT_TYPE_OK;
}
-
To check whether an SD card is mounted, the _fs_manager_is_mmc_supported() function is used where the storage_get_external_memory_size() function from the storage API is called. The total size of the external storage is counted. If the size is 0, no SD card is mounted.
For getting a list of the files and subfolders in the current folder, the fs_manager_get_file_list() function is called. Inside this, the model_utils_read_dir() function is called. It gets 2 Eina_List containers with the current folder's subfolders and files. Using the eina_list_sort() function sorts the lists. The order can be defined using the _fs_manager_sort_by_name_cb() function and merged into the result list using the eina_list_merge() function.
int
fs_manager_get_file_list(fs_manager *manager, const char *dir_path, Eina_List **file_list)
{
Eina_List *dirs = NULL;
Eina_List *files = NULL;
ret = model_utils_read_dir(dir_path, &dirs, &files);
if (ret != RESULT_TYPE_OK)
{
ERR("Failed to read dir '%s'", dir_path);
return ret;
}
dirs = eina_list_sort(dirs, eina_list_count(dirs), _fs_manager_sort_by_name_cb);
files = eina_list_sort(files, eina_list_count(files), _fs_manager_sort_by_name_cb);
*file_list = eina_list_merge(dirs, files);
return RESULT_TYPE_OK;
}
-
The folder content getter, the model_utils_read_dir() function uses the Linux readdir_r() function to read data from folders.
int
model_utils_read_dir(const char *dir_path, Eina_List **dir_list, Eina_List **file_list)
{
struct dirent ent_struct;
struct dirent *ent = NULL;
while ((readdir_r(pDir, &ent_struct, &ent) == 0) && ent)
{
int skip = (!ent->d_name ||
(strncmp(ent->d_name, ".", 1) == 0) ||
(strncmp(ent->d_name, "..", 2) == 0));
skip = skip || ((ent->d_type != DT_DIR) && (ent->d_type != DT_REG));
skip = skip || ((ent->d_type == DT_DIR) &&
(strcmp(dir_path, FM_PHONE_FOLDER) == 0) &&
(strcmp(ent->d_name, FM_DEBUG_FOLDER) == 0));
node_info *const pNode = skip ? NULL : calloc(1, sizeof(node_info));
if (pNode)
{
pNode->parent_path = strdup(dir_path);
pNode->name = strdup(ent->d_name);
pNode->is_selected = EINA_FALSE;
if (ent->d_type == DT_DIR)
{
pNode->type = FILE_TYPE_DIR;
}
else
{
model_utils_get_file_category(ent->d_name, &(pNode->type));
}
if (pNode->type == FILE_TYPE_DIR)
{
*dir_list = eina_list_append(*dir_list, pNode);
}
else
{
*file_list = eina_list_append(*file_list, pNode);
}
}
}
closedir(pDir);
return RESULT_TYPE_OK;
}
-
To create a new folder in the file system, use the fs_manager_create_folder() function that uses the Linux mkdir() method.
int
fs_manager_create_folder(fs_manager *manager, const char *dir)
{
RETVM_IF(!manager, RESULT_TYPE_INVALID_ARG, "File manager is NULL");
RETVM_IF(!dir, RESULT_TYPE_INVALID_ARG, "Directory path is NULL");
if (mkdir(dir, DIR_MODE) < 0)
{
ERR("Failed to create folder '%s'", dir);
return RESULT_TYPE_FAIL;
}
return RESULT_TYPE_OK;
}
-
To create a copy, or move or delete an operation, the fs_manager provides the fs_manager_copy_files(), fs_manager_move_files(), and fs_manager_delete_files() functions. All of them call the private _fs_manager_generate_operation() function. In this method, the fs_operation instance is created.
static int
_fs_manager_generate_operation(fs_manager *manager, Eina_List *source_list, const char *dest_path,
operation_type oper_type, fs_manager_complete_cb_func func, void *data)
{
manager->user_cb_func = func;
manager->user_cb_data = data;
manager->operation = fs_operation_create();
if (!manager->operation)
{
_fs_manager_clear_data(manager);
ERR("Failed to allocate memory for file operation");
return RESULT_TYPE_FAIL_ALLOCATE_MEMORY;
}
int result = fs_operation_set_data(manager->operation, source_list, dest_path, oper_type);
if (result != RESULT_TYPE_OK)
{
_fs_manager_clear_data(manager);
ERR("Failed to set operation data");
return result;
}
fs_operation_cb_data *cb_data = calloc(1, sizeof(fs_operation_cb_data));
if (!cb_data)
{
_fs_manager_clear_data(manager);
ERR("Failed to allocate memory for callback operation data");
return RESULT_TYPE_FAIL_ALLOCATE_MEMORY;
}
cb_data->manager = manager;
cb_data->result = RESULT_TYPE_FAIL;
// Lock file system manager
manager->is_locked = EINA_TRUE;
result = fs_operation_execute(manager->operation, _on_operation_completed, cb_data);
if (result != RESULT_TYPE_OK)
{
free(cb_data);
_fs_manager_clear_data(manager);
manager->is_locked = EINA_FALSE;
ERR("Failed to execute operation");
}
return result;
}
-
Every file operation that is needed to be done runs separately from the main loop thread. This functionality is provided by the fs_operation API. The fs_manager creates the fs_operation instance, sets the operation data, completes the callback data, and runs this operation by calling the fs_operation_execute() function where a new instance of the ecore_thread is created.
int
fs_operation_execute(fs_operation *operation, fs_operation_cb_func cb_func, fs_operation_cb_data *cb_data)
{
RETVM_IF(!operation, RESULT_TYPE_INVALID_ARG, "Operation object is NULL");
RETVM_IF(!operation->source_list, RESULT_TYPE_FAIL,"File list not set");
RETVM_IF(operation->oper_type == OPERATION_TYPE_NONE, RESULT_TYPE_FAIL,"Type of operation not set");
RETVM_IF(!operation->dst_path && (operation->oper_type != OPERATION_TYPE_DELETE),
RESULT_TYPE_FAIL, "Destination path not set");
operation->cb_func = cb_func;
operation->cb_data = cb_data;
operation->exec_thread = ecore_thread_feedback_run(_fs_operation_run, NULL,
_fs_operation_end, NULL,
(void*)operation, EINA_TRUE);
RETVM_IF(!operation->exec_thread, RESULT_TYPE_FAIL, "Failed to create thread");
return RESULT_TYPE_OK;
}
-
The _fs_operation_run() function is the thread function that runs in the separate thread. The operation is executed in this function file. The result of the operation is set to the callback data.
void
_fs_operation_run(void *data, Ecore_Thread *thread)
{
fs_operation *operation = data;
int res = RESULT_TYPE_FAIL;
switch (operation->oper_type)
{
case OPERATION_TYPE_COPY:
res = _fs_operation_copy(operation);
break;
case OPERATION_TYPE_MOVE:
res = _fs_operation_move(operation);
break;
case OPERATION_TYPE_DELETE:
res = _fs_operation_delete(operation);
break;
default:
ERR("Operation type not set");
return;
break;
}
if (operation->is_canceled)
{
return;
}
if (operation->cb_data)
{
operation->cb_data->result = res;
}
}
-
The _fs_operation_end() function runs after the _fs_operation_run() is finished and it runs in the main loop. In this method, the operation callback function is called and the operation result is returned to the operation caller.
void
_fs_operation_end(void *data, Ecore_Thread *thread)
{
fs_operation *operation = data;
_fs_operation_delete_exec_thread(operation);
if (operation->is_canceled)
{
fs_operation_destroy(operation);
return;
}
if (operation->cb_func)
{
operation->cb_func(operation->cb_data);
}
}