Friday 27 March 2015

How to Pull File from an Android App

The other day when I tested my Apache Cordova based app, I tried to get a file from my Android phone to test that the file was created correctly by the app. I wanted to copy the file to a location somewhere in my Windows based desktop. Below are the steps of how I do this:
- open a command prompt
- enter 'adb shell', you might need to run this inside a folder that has adb.exe if that is not accessible globally
- enter 'run-as    your.application.name'
- enter 'chmod 777    targeted_File_Path' in order to give full access to the file
So after entering these commands, the command prompt would look like this:
C:\Users\Me>adb shell
shell@zara:/ $ run-as your.application.name
run-as your.application.name
shell@zara:/data/data/your.application.name $ chmod 777 /data/data/your.application.name/files/filename.jpg
data/data/your.application.name/files/filename.jpg                   <
shell@zara:/data/data/your.application.name $ 
- quit the adb shell or open a new command prompt window
- enter 'adb pull    targeted_File_Path    destination_Desktop_Folder'
On command prompt, it will be something like:
C:\Users\Me>adb pull /data/data/your.application.name/files/filename.jpg c:/DestinationDirectory
2097 KB/s (19335 bytes in 0.009s)


References:
http://stackoverflow.com/questions/13006315/how-to-access-data-data-folder-in-android-device
http://www.codeproject.com/Articles/825304/Accessing-internal-data-on-Android-device

Thursday 19 March 2015

Example of Writing Image to File in Apache Cordova

Below is an example of how to writing image to a file in Apache Cordova. To have more understanding of basic file operations, please see my previous post.
window.resolveLocalFileSystemURL(my_Directory_Path,
function (dirEntry) {
 dirEntry.getFile(path_Of_File_To_Be_Written, { create: true }, 
  // getFile() success
  function (fileEntry) {
   fileEntry.createWriter(
    // createWriter() success
    function (fileWriter) {
     fileWriter.onwriteend = function (e) {
      . . .
     };

     fileWriter.onerror = function (e) {
      . . .
     };

     fileWriter.write(dataURIToBlob(getBase64Image(image_To_Be_Written, "image/jpeg")));
     // or if we already have a canvas
     //fileWriter.write(dataURIToBlob(canvas.toDataURL("image/jpeg")));
    }, 
    // createWriter() error
    function (error) {
     . . .
    }
   );
  },
  // getFile() error
  function (error) {
   . . .
  }
 );
});

A function to convert an image's base-64 encoded data to Blob type (this is taken from http://stackoverflow.com/questions/12391628/how-can-i-upload-an-embedded-image-with-javascript):
function dataURIToBlob(dataURI) {
 // serialize the base64/URLEncoded data
 var byteString;
 if (dataURI.split(',')[0].indexOf('base64') >= 0) {
  byteString = atob(dataURI.split(',')[1]);
 }
 else {
  byteString = unescape(dataURI.split(',')[1]);
 }

 // parse the mime type
 var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]

 // construct a Blob of the image data
 var array = [];
 for (var i = 0; i < byteString.length; i++) {
  array.push(byteString.charCodeAt(i));
 }
 return new Blob(
  [new Uint8Array(array)],
  { type: mimeString }
 );
}
Also a function to get base-64 encoded data from an image:
// type is either "image/png" or "image/jpeg"
function getBase64Image(img, type) {
    // create an empty canvas element
    var canvas = document.createElement("canvas");
    canvas.width = img.width;
    canvas.height = img.height;

    // copy the image contents to the canvas
    var ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0);

    // get the base-64 encoded data
    var dataURL = canvas.toDataURL(type);
 // toDataURL() actually has two optional parameters:
 // - type. The default value is 'image/png'.
 // - jpegQuality. A decimal value ranging from 0 to 1 to determine the quality of the data to be generated from a jpeg type image.
 //   The default value is browser dependant.

    return dataURL;
}

Monday 16 March 2015

Examples of File Operations in Apache Cordova

To be able to do any operation with directory/file in Apache Cordova, we need to access and get hold of the directory/file entry first. Then only after that we will be able to do any directory/file operation. This could be a very different approach from other frameworks that we have used before in dealing with file and file system.


Creating and Writing File
Let start with an example to create and write a file:
window.resolveLocalFileSystemURL(cordova.file.dataDirectory, function (dirEntry) {
 console.log("got directory entry", dirEntry);
 dirEntry.getFile("myFilename.txt", { create: true }, 
  // getFile() success
  function (fileEntry) {
   console.log("got file entry", fileEntry);
   fileEntry.createWriter(
    // createWriter() success
    function (fileWriter) {
     fileWriter.onwriteend = function (e) {
      console.log('write is successful');
      . . .
     };

     fileWriter.onerror = function (e) {
      console.log('write is failed', e);
      . . .
     };

     var blob = new Blob(['. . . some text . . .'], { type: 'text/plain' });
     fileWriter.write(blob);
    },
    // createWriter() error
    function (error) {
     . . .
    }
   );
  },
  // getFile() error
  function (error) {
   . . .
  }
 );
});

In the example above, we use resolveLocalFileSystemURL() function to get a directory or file entry from a directory/file system path (i.e. cordova.file.dataDirectory). resolveLocalFileSystemURL() has two parameters:
- the first parameter is a directory or file path
- the second one is the callback function to be executed once it succeed. A directory or file reference entry object is passed to the callback function.

Once we have hold of the file or directory entry object, we can do a number of operations such as creating, writing, reading, copying, moving, renaming or deleting the file. In this case, we want to create a new file and write into it. To do this, we need to call getFile() function first. The function has four parameters:
- the filename or file path
- function options (optional parameter). It has two properties with boolean values, namely; create and exclusive. create: true will create a file if it does not exist. To make the function throws an error if the file exists, set exclusive: true.
- on success callback function, passing a file reference entry object
- on error callback function (optional parameter), an error object is passed to the callback
Then after we get the file entry object, we call createWriter() function that will create a file writer object once successful. Next we call its write() function. The file writer object also has onwriteend() and onerror() callback functions.


Reading File
window.resolveLocalFileSystemURL(myFilePath, function (dirEntry) {
 fileEntry.file(
  // success
  function(file) {
     var reader = new FileReader();

     reader.onloadend = function(e) {
       console.log('file is read');
       . . .
     };

     reader.readAsText(file);
  }, 
  // error
  function (error) {
    . . .
  }
 );
});
In the example, we see that after we get a file reference entry object, we use file() method in order to create a file object of the intended file to be passed to FileReader readAsText() function.


Deleting File
window.resolveLocalFileSystemURL(myFilePath, 
 function (fileEntry) { 
  fileEntry.remove(
   // success
   function () { 
    console.log('file is removed'); 
    . . .
   }, 
   // error
   function (error) {
    . . .
   }
  ); 
 } 
); 
In this last example, we call file entry object's remove() function to delete a file.

Thursday 5 March 2015

Cropping Image with JavaScript and Canvas

Below is an example of how to crop image using JavaScript and HTML Canvas element.
var canvas = document.createElement('canvas');
canvas.width = 150;
canvas.height = 150;
//var canvas = document.getElementById('myCanvas');  // create a new canvas or use the existing one in document

var context = canvas.getContext('2d');

var sourceImg = new Image();

sourceImg.onload = function () {    // make sure the image is loaded first before cropping it

 context.drawImage(sourceImg, source_starting_x_coord, source_starting_y_coord, 
  width_to_be_cropped_from_source_coord, height_to_be_cropped_from_source_coord, 
  starting_x_coord_on_destination_canvas, starting_y_coord_on_destination_canvas, 
  width_of_cropped_image_to_put_on_canvas, heigth_of_cropped_image_to_put_on_canvas);
 //context.drawImage(sourceImg, 0, 50, 150, 150, 0, 0, 150, 150);

 // display or do anything we like with the result
 //imageDisplay = canvas.toDataURL();

}

sourceImg.src = toBeCroppedImageSource;  // can be an image url or base-64 encoded data