Zappescu Zappescu - 2 months ago 12
Javascript Question

FileOpenPicker WP8.1 - How to

I'm new in windows phone development and I have to port my app on it, using phonegap/cordova. Most of my code works for Android/iOS and also winphone, but on this FileOpenPicker I'm blocked.
I'm using winjs 2.1 and I'd like to prepare a script to be called when I'm in a page needing this functionality.

I've read a ton of examples, and I think I'm pretty near the solution.

In my html file I declare:

<script src="//Microsoft.Phone.WinJS.2.1/js/base.js"></script>
<script src="//Microsoft.Phone.WinJS.2.1/js/ui.js"></script>
<script type="text/javascript" src="js/default.js"></script>


And This is my default.js, the file I use in the page where the FileOpenPicker have to be called.

(function () {
"use strict";

var app = WinJS.Application;
var activation = Windows.ApplicationModel.Activation;

app.onloaded = function (args) {
var activationKind = args.detail.kind;

document.getElementById("btnSnap").addEventListener("click", pickSinglePhoto);

if (activationKind === Windows.ApplicationModel.Activation.ActivationKind.pickFileContinuation) {
continueFileOpenPicker(options.activatedEventArgs);
}
};

function pickSinglePhoto() {
// Clean scenario output
WinJS.log && WinJS.log("", "sample", "status");
console.log("in pickSinglePhoto");

// Create the picker object and set options
var openPicker = new Windows.Storage.Pickers.FileOpenPicker();
openPicker.viewMode = Windows.Storage.Pickers.PickerViewMode.thumbnail;
openPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.picturesLibrary;
// Users expect to have a filtered view of their folders depending on the scenario.
// For example, when choosing a documents folder, restrict the filetypes to documents for your application.
openPicker.fileTypeFilter.replaceAll([".png", ".jpg", ".jpeg"]);

// Open the picker for the user to pick a file
openPicker.pickSingleFileAndContinue();
}

// Called when app is activated from file open picker
// eventObject contains the returned files picked by user
function continueFileOpenPicker(eventObject) {
console.log("in continueFileOpenPicker");
var files = eventObject[0].files;
var filePicked = files.size > 0 ? files[0] : null;
if (filePicked !== null) {
// Application now has read/write access to the picked file
WinJS.log && WinJS.log("Picked photo: " + filePicked.name, "sample", "status");
} else {
// The picker was dismissed with no selected file
WinJS.log && WinJS.log("Operation cancelled.", "sample", "status");
}
}

app.start();
})();


Unfortunately, this doesn't work. I'm not able to go into continueFileOpenPicker because the flag activationKind is always undefined. I'm pretty sure I should use app.onactivated instead of app.onloaded, but in the former case I'm not able to go into the function.

I already tried the function pickSinglePhoto and it seems to work, but I can't come back to the page after the selection since the app crashes, clearly because I can't pick and use the function in my others javascript files as a brute.

Any clue?

Answer

I finally got it. This is my working solution based on the official answer from the Cordova Camera Plugin JIRA issues(link).

  1. Use official camera plugin Cordova (link)
  2. Main files: index.html, index.js, new.html, new.js, wp_get_image.js, utils.js.

First, in the index.html (my main page) I declared the wp_get_image.js script:

<script type="text/javascript" src="js/localstoragedb.min.js"></script>
<script type="text/javascript" src="js/utils.js"></script>
<script type="text/javascript" src="js/wp_get_image.js"></script> 
<script type="text/javascript" src="js/index.js"></script>

Remember:

"In short, choosing image from gallery causes app to suspend until image is picked and then resume with start page (as defined in config.xml). If you have called getPicture from different page, this will cause the whole app to reload and open start page. In addition, callbacks to getPicture also get wiped in this case so you'll never get any result from the plugin."

So, I need an hook on the index page to intercept the callback from the camera success. This is my wp_get_image.js:

var goto_new_page_winphone = "";

(function () {
    "use strict";

    var app = WinJS.Application;
    var activation = Windows.ApplicationModel.Activation;

    app.onactivated = function (args) {

        if (args.detail.kind === activation.ActivationKind.launch) {
            var that = this;

            // Init:
            goto_new_page_winphone = "";
            localStorage.setItem("image_url_winphone", "");
        }

        if (args && args.detail.kind === activation.ActivationKind.pickFileContinuation) {
            continueFileOpenPicker(args);
        };

        args.setPromise(WinJS.UI.processAll().then(function () {
            // Text
        }));
    };

    function continueFileOpenPicker(eventObject) {

        console.log("in continueFileOpenPicker");
        var filePicked = eventObject.detail.files[0];
        var msgBox;
        if (filePicked !== null) {
            // Save filePicked.path in localstorage:
            localStorage.setItem("image_url_winphone", filePicked.path);
        } else {
            msgBox = new Windows.UI.Popups.MessageDialog("Operation cancelled.");
            msgBox.showAsync();
        }
        // Save back page:
        goto_new_page_winphone = read_backpage();
    };
app.start();
})();

I use localstorage to save the image url and a local variable to store the page from which I came (the page I called new.html). The read_backpage() function is declared in my utils.js file, in which I track the pages visited by the user. So, after the action on the new.html page where I take the picture, the system kick me back on the index page. There, I need to read the page from which I came. So in my index.js:

window.addEventListener("load", load, false);

function load() {
    if (device.platform != undefined) {
        var plat = device.platform;
        plat = plat.substring(0, 3);
        if (plat.toLowerCase() == "win") {
            if (goto_new_page_winphone && goto_new_page_winphone.length > 0) {
                window.location.href = goto_new_page_winphone;
            }
        }
    }
}

May attention: the flow is: $(document).ready in index.js -> app.onactivated in wp_get_image.js -> load in index.js. So don't put the goto function (window.location.href = goto_new_page_winphone) inside the $(document).ready, otherwise you will have the var goto_new_page_winphone null and you can't move.

Well, at this point, you have choosen the picture in the new.html page, came back to index.html but now you can hopefully redirect the app to the page declared into goto_new_page_winphone (in my case the page is called new.html).

In new.js (the script related to new.html) I pick up the path of the image:

if (is_winphone) {
        var image_url = localStorage.getItem("image_url_winphone");
        if (image_url && image_url.length > 0) {
            upload_img("newprofile", image_url, function (urlimg) {
                // Reset localstorage var:
                localStorage.setItem("image_url_winphone", "");
                if (urlimg.length > 0) {
                    urlimg = urlimg.replace(/\\/g, '');
                    console.log(urlimg);
                    // Save remote url in my localstorage
                    wp_save_img_url(urlimg);
                }
            });
        }
    }

In my case, I send the image to my server via the upload_img func and the server answer with the remote url I save for my purposes. The upload img func is a simple function you can build following the File Tranfer Plugin doc.