The Mega App model and API

The Mega App model contains only one JavaScript object that represents the voice and photo memo data, as shown in the following code snippet:

var MemoItem = function(memoItem) {
    this.id = memoItem.id || "Memo_" + (new Date()).getTime();
    this.title = memoItem.title || "";
    this.desc = memoItem.desc || "";
    this.type = memoItem.type || "voice";
    this.location = memoItem.location || "";
    this.mtime = memoItem.mtime || "";
};

The MemoItem object contains the following attributes:

  • id: This represents the memo ID (its default value is unique as it includes a numeric value of the current time in milliseconds)
  • title: This represents the memo title
  • desc: This represents the memo description
  • type: This represents the memo type, and it can be "voice" or "photo" (its default value is "voice")
  • location: This represents the location of the media (audio or photo) file in the device's filesystem
  • mtime: This represents the time when the memo was created

We mainly have one service (MemoManager) that is used by the app view controllers. The MemoManager object contains the API needed to:

  • Save a memo
  • Update a memo
  • Remove a memo
  • Remove all memos
  • Get memo details
  • Get all memos
  • Record and play a voice
  • Get a photo from the camera or gallery

The MemoManager object uses FileManager in order to perform the required file operations, which will be illustrated later.

The following code snippet shows the first part of the MemoManager object:

var MemoManager = (function () {     
    var instance;
 
    function createObject() {     
        var MEMOS_KEY = "memos";
        var APP_BASE_DIRECTORY = "Mega";
        var audioMedia;
        var recordingMedia;
        var mediaFileName;
      
        return {
            getMemos: function () {
                var items = window.localStorage.getItem(MEMOS_KEY);
              
                if (items) {
                    memoMap = JSON.parse(items);
                } else {
                    memoMap = {};
                }
              
                return memoMap;
            }, 
            getMemoDetails: function (memoID) {
                var memoMap = this.getMemos();
              
                return memoMap[memoID];
            },
            saveMemo: function (memoItem) {  
                var memoMap = this.getMemos();
              
                memoMap[memoItem.id] = memoItem;
              
                window.localStorage.setItem(MEMOS_KEY, JSON.stringify(memoMap));
            }, 
            removeMemo: function(memoID) {
                var memoMap = this.getMemos();
              
                if (memoMap[memoID]) {
                    delete memoMap[memoID];
                }
              
                window.localStorage.setItem(MEMOS_KEY, JSON.stringify(memoMap));
            },
            removeAllMemos: function() {
                window.localStorage.removeItem(MEMOS_KEY);
            }
          
            // code is omitted for simplicity ...
        };
    };
 
    return {
        getInstance: function () {
            if (!instance) {
                instance = createObject();
            }
 
            return instance;
        }
    }; 
})();

As shown in the preceding code, the first part of MemoManager is straightforward. It has the following methods, which are used to save, update, delete, and retrieve memos:

  • saveMemo(memoItem): This uses the window.localStorage.setItem() method to save or update a memo item in the device's Local Storage by adding (updating) it to the memoMap object. The memoMap object is a JSON map whose key represents the memo item ID and value represents the memo item object.
  • removeMemo(memoID): This removes the memo, whose ID is memoID, from memoMap and finally saves the updated memoMap object in the device's local storage using the window.localStorage.setItem() method.
  • removeAllMemos(): This uses the window.localStorage.removeItem() method to remove memoMap from the device's local storage.
  • getMemoDetails(memoID): This gets the memo details from memoMap using memoID.
  • getMemos(): This gets all the app's memos by returning the stored memoMap object. The stored memoMap object is retrieved from the device's local storage using the window.localStorage.getItem() method.

The following code snippet shows the voice recording and playback parts of MemoManager:

startRecordingVoice: function (recordingCallback) {
    var recordVoice = function(dirPath) {
        var basePath = "";

        if (dirPath) {
            basePath = dirPath;
        }

        mediaFileName = (new Date()).getTime() + ".wav";
      
        var mediaFilePath = basePath + mediaFileName;
      
        var recordingSuccess = function() {
            recordingCallback.recordSuccess(mediaFilePath);
        };            
        recordingMedia = new Media(mediaFilePath, recordingSuccess, recordingCallback.recordError);

        // Record audio
        recordingMedia.startRecord(); 
    };
  
    if (device.platform === "Android") {
      
        // For Android, store the recording in the app directory under the SD Card root if available ...
        var callback = {};
  
        callback.requestSuccess = recordVoice;
        callback.requestError = recordingCallback.recordError;

        fileManager.requestDirectory(APP_BASE_DIRECTORY, callback);
    } else if (device.platform === "iOS") {
       
        // For iOS, store recording in app documents directory ...
        recordVoice("documents://");
    } else {
      
        // Else for Windows Phone 8, store recording under the app directory
        recordVoice();
    }
},
stopRecordingVoice: function () {
    recordingMedia.stopRecord();   
    recordingMedia.release();
},
playVoice: function (filePath, playCallback) {
    if (filePath) {                  
        this.cleanUpResources();
         
        audioMedia = new Media(filePath, playCallback.playSuccess, playCallback.playError);
    
        // Play audio
        audioMedia.play();
    }            
},
cleanUpResources: function() {
    if (audioMedia) {
        audioMedia.stop();
        audioMedia.release();
        audioMedia = null;
    } 
              
    if (recordingMedia) {
        recordingMedia.stop();
        recordingMedia.release();
        recordingMedia = null;
    } 
}

The startRecordingVoice(recordingCallback) method, starts the voice recording action, checks the current device's platform in order to save the audio file properly:

  • If the current platform is Android, then it requests the app directory path ("Mega") in order to save the recorded media file under it. In order to do this, a call to fileManager.requestDirectory(APP_BASE_DIRECTORY, callback) is performed in order to get the app directory path and optionally create it if it does not exist under the device's SD card root using the Apache Cordova file API (or create it under the app's private data directory, (/data/data/[app_directory], if the SD card is not available). If the app directory request operation succeeds, then recordVoice(dirPath) will be called, in this case, and the app directory path (dirPath) will be passed as a parameter. The recordVoice() function starts recording the voice using the Media object's startRecord() method. In order to create a Media object, the following parameters are specified in the Media object constructor:
    • The complete path of the media file (mediaFilePath).
    • recordingSuccess which refers to the callback that will be invoked if the media operation succeeds. recordingSuccess calls the original recordingCallback.recordSuccess method specifying mediaFilePath as a parameter.
    • recordingCallback.recordError which refers to the callback that will be invoked if the media operation fails.
  • In iOS, recordVoice() is called with "documents://" as the directory path (dirPath) in order to save our app's media audio file under the Documents directory of the app's Sandbox directory.

    Note

    In iOS, if we just specify the audio filename without any path in the Media object constructor:

    var recordingMedia = new Media("test.wav" ...);
    

    Then "test.wav" will be saved under the tmp directory of our iOS app's Sandbox directory (if you do this, then your app users will be surprised to find, maybe after a while, that their saved audio files have been deleted as the tmp directory can be cleaned automatically by iOS, so be aware of this).

    Specifying the "documents://" prefix before the media filename in the Media object constructor will enforce the Media object to save the "test.wav" file under the app's Documents directory:

    var recordingMedia = new Media("documents://test.wav" ...);
  • In the final else block that represents the Windows Phone 8 case, recordVoice() is called without specifying any parameters that tell the Media object to save the audio file under the app local directory.

As you can see, we have to respect the nature of every supported platform in order to get the expected results.

The stopRecordingVoice() method simply stops recording the voice by calling the stopRecord() method of the Media object and finally releases the used media resource by calling the release() method of the Media object.

The playVoice(filePath, playCallback) method creates a Media object that points to the file specified in filePath, and then plays the file using the play() method of the Media object. The cleanUpResources() method makes sure that all of the used media resources are cleaned up.

The following code snippet shows the photo capturing and picking part of MemoManager:

getPhoto: function (capturingCallback, fromGallery) {      
    var source = Camera.PictureSourceType.CAMERA;
  
    if (fromGallery) {
        source = Camera.PictureSourceType.PHOTOLIBRARY;  
    }
  
    var captureSuccess = function(filePath) {
        //Copy the captured image from tmp to app directory ...
        var fileCallback = {};
      
        fileCallback.copySuccess = function(newFilePath) {
            capturingCallback.captureSuccess(newFilePath);
        };
       
        fileCallback.copyError = capturingCallback.captureError;
       
        if (device.platform === "Android") {
       
            //If it is Android then copy image file to App directory under SD Card root if available ...
            fileManager.copyFileToDirectory(APP_BASE_DIRECTORY, filePath, true, fileCallback);
        } else if (device.platform === "iOS") {
       
            //If it is iOS then copy image file to Documents directory of the iOS app.
            fileManager.copyFileToDirectory("", filePath, true, fileCallback);
        } else {
       
            //Else for Windows Phone 8, store the image file in the application's isolated store ...
            capturingCallback.captureSuccess(filePath);
        }
    };
    navigator.camera.getPicture(captureSuccess,
        capturingCallback.captureError, 
        { 
            quality: 30, 
            destinationType: Camera.DestinationType.FILE_URI, 
            sourceType: source,
            correctOrientation: true
        });              
}

The getPhoto(capturingCallback, fromGallery)method, is used to get the photo by picking it from the device's gallery or by capturing it using the device's camera, it checks the fromGallery parameter and if it is set to true, then the picture source type is set to Camera.PictureSourceType.PHOTOLIBRARY, and if it is set to false, the picture source type will be Camera.PictureSourceType.CAMERA.

A photo is obtained by calling the navigator.camera.getPicture() method specifying the following parameters in order:

  • captureSuccess: This refers to the callback that will be invoked if the getPicture() operation succeeds
  • capturingCallback.captureError: This refers to the callback that will be invoked if the getPicture() operation fails
  • In the last parameter, the camera options are set (destinationType is set to Camera.DestinationType.FILE_URI to get the image file URI, sourceType is set to the picture source type, quality is set to 30, and finally, correctOrientation is set to true)

captureSuccess checks the current device's platform to properly save the picture file:

  • If the current platform is Android, then it copies the picture file to the app directory. In order to do this, a call to the fileManager.copyFileToDirectory(APP_BASE_DIRECTORY, filePath, true, fileCallback) method is performed. The fileManager.copyFileToDirectory(dirPath, filePath, enforceUniqueName, fileCallback) method has the following parameters placed in order:
    • dirPath: This represents the full path of the destination directory to which the file will be copied.
    • filePath: This represents the full path of the file to be copied.
    • enforceUniqueName: If this parameter is set to true, this will enforce the copied file to have a new name in the destination directory.
    • fileCallback: This represents a callback object that includes the two callback attributes (copySuccess, which will be called if the copy file operation succeeds, and copyError, which will be called if the copy file operation fails).

    If the file-copy operation succeeds, then fileCallback.copySuccess will be called. In this case, fileCallback.copySuccess calls capturingCallback.captureSuccess(newFilePath), specifying the new copied file path (newFilePath) as a parameter.

  • In iOS, the image file is copied to the app's Documents directory. In order to do this, a call to fileManager.copyFileToDirectory("", filePath, true, fileCallback) is performed.

    Tip

    In iOS, when any photo is captured using the device camera or picked from the device gallery using navigator.camera.getPicture(), it is placed under the tmp directory of the iOS app's Sandbox directory. If you want to access the captured image later, make sure to copy or move the captured image to the app's Documents directory as shown earlier.

  • Finally, in the final else block that represents the Windows Phone 8 case, there is no need to do any file copy, as the image file (captured using the camera or picked from the device gallery) is automatically placed under the app local directory.

FileManager is very similar to the FileManager object discussed in Chapter 5, Diving Deeper into the Cordova API (refer to this chapter if you feel uncomfortable with the highlighted code in the following code snippet):

var FileManager = (function () {     
    var instance;
 
    function createObject() {
         var FILE_BASE = "file:///";
      
         return {
             copyFileToDirectory: function (dirPath, filePath, enforceUniqueName, fileCallback) {
                 var directoryReady = function (dirEntry) { 
                     if (filePath.indexOf(FILE_BASE) != 0) {
                         filePath = filePath.replace("file:/", FILE_BASE);
                     }
              
                     window.resolveLocalFileSystemURL(filePath, function(file) {
                         var filename = filePath.replace(/^.*[\/]/, ''),
                             
                         if (enforceUniqueName) {
                             console.log("file name before: " + filename);
                             filename = (new Date()).getTime() + filename;
                             console.log("file name after: " + filename);
                         }
                                      
                         file.copyTo(dirEntry, filename, function(fileEntry) {
                             fileCallback.copySuccess(dirEntry.toURL() + filename);
                         }, fileCallback.copyError);
                     }, fileCallback.copyError);  
                 };              
              
                 var fileSystemReady = function(fileSystem) {
                     fileSystem.root.getDirectory(dirPath, {create: true}, directoryReady);
                 };
                            
                 window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, fileSystemReady, fileCallback.copyError);
             },
             requestDirectory: function (dirPath, callback) {
                 var directoryReady = function (dirEntry) {
                     callback.requestSuccess(dirEntry.toURL());
                 };
                  
                 var fileSystemReady = function(fileSystem) {
                     fileSystem.root.getDirectory(dirPath, {create: true}, directoryReady);                    
                 };              
                  
                 window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, fileSystemReady, callback.requestError);
             }
        };
    };
 
    return {
        getInstance: function () {
            if (!instance) {
                instance = createObject();
            }
     
            return instance;
        }
    }; 
})();

Now that we are done with the model and API code of our Mega App, the next section will illustrate the Mega App's pages and view controllers.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.125.51