Task: File Manager
This task, based on the FileManager sample delivered with the Tizen SDK, demonstrates how you can use the Filesystem API (in mobile and wearable applications) to manage files in your application. For more information on the sample functionality and creating the sample with the full source code, see FileManager.
This task consists of the following parts:
- Defining the Application Layout defines how to create the application screens.
- Managing Clipboard defines how to work with the clipboard.
- Managing Device Storage defines how to create, copy, move, and remove folders and files.
This sample is a fully functional application for browsing the device storage. The user can create, copy, move, and remove folders and files.
Defining the Application Layout
The FileManager sample application layout uses the template manager based on the MVC (Model-View-Controller) architecture, and consists of 1 screen that displays the device storage folder and file structure.
Additionally, the sample application layout consists of 2 UI elements: the clipboard footer controls enable you to delete, move, or copy selected folders and files, and the More pop-up window enables you to create a new folder and to paste content to a folder.
The following figure shows the main screens of the application.
Figure: FileManager screen
Using the Template Manager
The template manager enables the HTML output generation to be divided into 3 parts.
- app.ui.templateManager.js Source File
-
The template manager loads the template files into the cache.
loadToCache: function TemplateManager_loadToCache(tplNames, onSuccess) { var self = this, cachedTemplates = 0, tplName, tplPath; if ($.isArray(tplNames)) { $.each(tplNames, function(index, fileName) { if (self.cache[fileName] === undefined) { tplName = [fileName, app.config.get('templateExtension')].join(''); tplPath = [app.config.get('templateDir'), tplName].join('/'); $.ajax( { url: tplPath, cache: true, dataType: 'html', async: true, success: function(data) { cachedTemplates += 1; self.cache[fileName] = data; if (cachedTemplates >= tplNames.length && typeof onSuccess === 'function') { onSuccess(); } }, /* Error handling */ }); } else { cachedTemplates += 1; if (cachedTemplates >= tplNames.length && typeof onSuccess === 'function') { onSuccess(); } } }); } }
-
Next, the template manager returns the template HTML content from the cache.
get: function TemplateManager_get(tplName, tplParams) { if (this.cache[tplName] !== undefined) { return this.getCompleted(this.cache[tplName], tplParams); } return ''; }
-
The getCompleted() method returns the completed template using the specified parameters, prepared by the passThruModifiers() method.
getCompleted: function TemplateManager_getCompleted(tplHtml, tplParams) { var tplParam, replaceRegExp; for (tplParam in tplParams) { if (tplParams.hasOwnProperty(tplParam)) { tplHtml = this.passThruModifiers(tplHtml, tplParam, tplParams[tplParam]); } } return tplHtml; } passThruModifiers: function(tplHtml, tplParam, content) { var regModOn = new RegExp('%' + tplParam + '\\|([a-zA-Z]){1,}%', 'g'), regModOff = new RegExp(['%', tplParam, '%'].join(''), 'g'), regModGet = new RegExp('%' + tplParam + '\\|(.+?)%'), specRegExp = new RegExp('\\$', 'g'), modifier; if (content && (typeof content === 'string')) { content = content.replace(specRegExp, '$$$$'); } if (regModOn.test(tplHtml)) { modifier = tplHtml.match(regModGet)[1]; try { content = this.modifiers[modifier](content); } tplHtml = tplHtml.replace(regModOn, content); } else { tplHtml = tplHtml.replace(regModOff, content); } return tplHtml; }
-
Defining the Main Screen
The main screen elements initialization and display functionality is implemented in the app.ui.js file.
- main.tpl Source File
The main screen of the application displays the device storage folder and file structure. The header section of the screen is defined within a <div> element whose data-role attribute is set to header. The header section determines the title of the screen.
If the user navigates to another child directory, the header section displays navigation buttons - Up and Home - to move 1 directory up in the file system and to move to the root directory, respectively. In a child directory, the breadcrumb navigation from the root directory to the current directory is displayed as well.
<!--Header section--> <div data-role="header" data-position="fixed"> <h1 id="mainTitle"></h1> <!--Other application code--> <a id="homeBtn">Home</a> <a id="levelUpBtn">Up</a> <!--Other application code--> <div id="navbar"></div> <div class="selectAll" style="display: none"></div> <!--Other application code--> </div>
- folderRow.tpl Source File
The folderRow.tpl template file defines the actual content of the main screen, which is initialized in the app.ui.js source file.
<!--Content section--> <li class="node folder ui-li-1line-bigicon1" id="row%id%" label="%name%" uri="%uri|escapeEncies%" fullUri="%fullUri|escapeEncies%"> <form class="my-ui-checkbox hidden"><input type="checkbox" /></form> <img src="images/folder.png" class="ui-li-bigicon" /> <span class="ui-li-text-main nodename">%name|escape%</span> </li>
Defining the Footer Controls
- main.tpl Source File
The footer section of the screen is defined within a <div> element whose data-role attribute is set to footer. The footer section contains a tab bar with a button for enabling the clipboard functionality in the application.
The clipboard consists of buttons for deleting, moving, and copying folders and files, and also canceling the clipboard operation. In the clipboard mode, check boxes appear next to the folders and files list in the current directory on the main screen.
<div data-role="footer" data-position="fixed"> <div class="ui-myToolBar"> <div data-role="tabbar" class="standardTabbar"> <ul> <li><a id="editActionBtn" href="#">Edit</a></li> <!--Other application code--> </ul> </div> <div data-role="tabbar" class="editTabbar" style="display: none"> <ul> <li><a id="deleteActionBtn" href="#">Delete</a></li> <li><a id="moveActionBtn" href="#">Move</a></li> <li><a id="copyActionBtn" href="#">Copy</a></li> <li><a id="cancelActionBtn" href="#">Cancel</a></li> </ul> </div> <!--Other application code--> </div> </div>
Defining the Pop-up Window
- main.tpl Source File
The pop-up window consists of a jQuery pop-up menu with options for creating a new folder at the current directory location or for pasting already copied content to the current directory location.
<div data-role="footer" data-position="fixed"> <div class="ui-myToolBar"> <div data-role="tabbar" class="standardTabbar"> <ul> <!--Other application code--> <li><a id="moreActionBtn" href="#morePopup" data-rel="popup">More</a></li> <!--Other application code--> </ul> </div> <div id="morePopup" data-role="popup"> <!--Other application code--> </div> </div> </div>
Managing Clipboard
This section builds upon the elements described in Managing Files and Directories.
Initializing the Clipboard
The clipboard features definition functionality is implemented in the app.clipboard.js file.
- Retrieving Clipboard Content
The get() method is defined to retrieve all the filesystem paths saved currently in the clipboard.
get: function Clipboard_get() { return this.data; }
- Adding New Content to the Clipboard
The add() method is defined to add filesystem paths to the clipboard.
add: function Clipboard_add(paths) { var len = paths.length, i; /* Clear clipboard */ this.clear(); for (i = 0; i < len; i += 1) { if (this.has(paths[i]) === false) { this.data.push(paths[i]); } } return this.data.length; }
- Checking if a Path is Present in the Clipboard
The has() method is defined to check whether the currently copied filesystem path is already present in the clipboard.
has: function Clipboard_has(path) { return $.inArray(path, this.data) === -1 ? false : true; }
- Setting the Clipboard Mode
The setMode() method is defined to set the clipboard in 3 available modes - copy, move, and inactive (default).
setMode: function Clipboard_setMode(mode) { if ($.inArray(mode, [this.MOVE_MODE_ID, this.COPY_MODE_ID]) === false) { /* Error handling */ } this.mode = mode; return true; }
- Clearing the Clipboard
The clear() method is defined to clear all the clipboard content and reset it to the default mode.
clear: function Clipboard_clear() { this.data = []; this.mode = this.INACTIVE_MODE; }
Performing Clipboard Operations
The clipboard operation functionality is implemented in the app.js file.
- Copying Content to the Clipboard
The saveToClipboard() method is used to add filesystem paths to the clipboard and set the appropriate clipboard mode.
saveToClipboard: function App_saveToClipboard(paths, mode) { var clipboardLength = this.clipboard.add(paths); if (clipboardLength > 0) { this.clipboard.setMode(mode); app.ui.alertPopup('Data saved in clipboard'); this.ui.clearTabbars(); } else { alert('Error occurred. Data has not been saved in clipboard'); } this.ui.refreshPasteActionBtn(this.clipboard.isEmpty()); }
- Pasting Content from the Clipboard
-
The pasteClipboard() method is used to copy filesystem paths from the clipboard or to move filesystem paths to the current directory location based on the current clipboard mode.
pasteClipboard: function App_pasteClipboard() { var clipboardData = this.clipboard.get(); if (clipboardData.length === 0) { app.ui.alertPopup('Clipboard is empty'); return false; } if (this.clipboard.getMode() === this.clipboard.COPY_MODE_ID) { this.model.copyNodes(this.currentDirHandle, clipboardData, this.currentPath, this.onPasteClipboardSuccess.bind(this)); } else { this.model.moveNodes(this.currentDirHandle, clipboardData, this.currentPath, this.onPasteClipboardSuccess.bind(this)); } this.ui.refreshPasteActionBtn(this.clipboard.isEmpty()); return true; }
-
The onPasteClipboardSuccess() event handler responds to the success event of pasting content from the clipboard by clearing the clipboard.
onPasteClipboardSuccess: function App_onPasteClipboardSuccess() { this.clipboard.clear(); this.refreshCurrentPage(); }
-
Managing Device Storage
This section builds upon the elements described in Managing File Storages, Creating and Deleting Files and Directories, and Retrieving Files and File Details.
Initializing the Device Storage
- config.xml Source File
The required privileges are declared in the config.xml file.
<!--Configuration file content--> <widget ...> <!--Other configuration details--> <tizen:privilege name="http://tizen.org/privilege/content.read"/> <tizen:privilege name="http://tizen.org/privilege/content.write"/> <tizen:privilege name="http://tizen.org/privilege/application.launch"/> <tizen:privilege name="http://tizen.org/privilege/filesystem.read"/> <tizen:privilege name="http://tizen.org/privilege/filesystem.write"/> <!--Other configuration details--> </widget>
Retrieving Storages
- app.model.js Source File
The loadInternalStorages() method calls the getStorages() method to get the storages of a specific storage type.
loadInternalStorages: function Model_loadInternalStorages(onSuccess) { var self = this; this.systemIO.getStorages('INTERNAL', function(storages) { self.storages = storages; if (typeof onSuccess === 'function') { onSuccess(); } }, 'internal0'); }
- app.systemIO.js Source File
The listStorages() method of the FileSystemManager interface (in mobile and wearable applications) is used to retrieve all the available storages.
getStorages: function SystemIO_getStorages(type, onSuccess, excluded) { try { tizen.filesystem.listStorages(function(storages) { var tmp = [], len = storages.length, i; if (type !== undefined) { for (i = 0; i < len; i += 1) { if (storages[i].label !== excluded) { if (storages[i].type === 0 || storages[i].type === type) tmp.push(storages[i]); } } } else { tmp = storages; } if (typeof onSuccess === 'function') onSuccess(tmp); }); } }
Managing Files and Directories
The directory creation functionality is implemented in the app.js file. The file creation, file deletion, and directory deletion functionality is implemented in the app.systemIO.js file.
- Creating a File
The createFile() method of the File interface (in mobile and wearable applications) is used to create a new file in the current directory.
createFile: function SystemIO_createFile(directoryHandle, fileName) { try { return directoryHandle.createFile(fileName); } }
Similarly, the createDirectory() method is used to create a directory at the current storage location in the application.
- Deleting a File
The deleteFile() method of the File interface is used to delete a file by specifying the filesystem path of the file.
deleteFile: function SystemIO_deleteFile(dir, filePath, onDeleteSuccess, onDeleteError) { try { dir.deleteFile(filePath, onDeleteSuccess, onDeleteError); } }
Similarly, the deleteDirectory() method is used to delete a selected directory. You can specify if the deletion is to be performed recursively for the sub-directories as well.
Retrieving File Details
The file and file list retrieval functionality is implemented in the app.systemIO.js file. The file URI retrieval functionality is implemented in the app.ui.js file.
- Retrieving a File
The resolve() method of the FileSystemManager interface is used to retrieve a file handle by specifying the location of the file.
openDir: function SystemIO_openDir(directoryPath, onSuccess, onError, openMode) { openMode = openMode || 'rw'; onSuccess = onSuccess || function() {}; try { tizen.filesystem.resolve(directoryPath, onSuccess, onError, openMode); } }
- Retrieving a List of Files
The listFiles() method of the FileSystemManager interface is used to list all the files and directories present in the current storage location in the application.
getFilesList: function SystemIO_getFilesList(dir, onSuccess) { try { dir.listFiles(function(files) { var tmp = [], len = files.length, i; for (i = 0; i < len; i += 1) { tmp.push(files[i].name); } if (typeof onSuccess === 'function') { onSuccess(tmp); } }, function(e) { console.error('SystemIO_getFilesList dir.listFiles() error:', e); }); } }
- Retrieving a Folder or File URI
The toURI() method of the FileSystemManager interface is used to retrieve the folder or file URI.
displayFolder: function Ui_displayFolder(folderName, nodes, refresh) { var len = nodes.length, listElements = [this.templateManager.get('levelUpRow')], nodeName, checkedRows = [], i; /* Other application code */ for (i = 0; i < len; i = i + 1) { nodeName = nodes[i].name.trim(); if (nodeName !== '') { if (nodes[i].isDirectory) { listElements.push(this.templateManager.get('folderRow', { id: i, name: nodeName, uri: nodes[i].fullPath, fullUri: nodes[i].toURI() })); } else { listElements.push(this.templateManager.get('fileRow', { id: i, name: nodeName, uri: nodes[i].fullPath, fullUri: nodes[i].toURI(), thumbnailURI: this.helpers.getThumbnailURI(nodeName, nodes[i]) })); } } } }