pwlin / cordova-plugin-file-opener2

A File Opener Plugin for Cordova
MIT License
316 stars 588 forks source link

showOpenWithDialog is not working #145

Closed agilethomas closed 5 years ago

agilethomas commented 7 years ago

I'm trying to use the showOpenWithDialog feature, but I can't get it to work for some reason.

I'm downloading the file successfully, because I can use fileopener2.open() and it will open the PDF in a new window. I can also see the request successfully completing on the server side when I call the showOpenWithDialog function, and the success callback is also getting called. But nothing happens on the UI.

I found this recent issue that seemed related:

https://github.com/pwlin/cordova-plugin-file-opener2/issues/132

I don't use Xcode that much (we build our app using cordova's command line), but I was able to find the "Auto layout" setting in the MainViewController.xib file under Classes/ in the File Navigator. That's the only .xib file I found. That setting is already unchecked in the File Inspector. I tried checking it and rebuilding it, but that did not have any effect.

I also found another "Auto layout" setting in the Resources > CDVLaunchScreen.storyboard, but unchecking Auto Layout did not have any effect.

I've upgraded Cordova to 7.0.1 and am using XCode 8.2.1. This is for an iPad app only. It's minimum target is iOS 9.0 and I'm running 9.3.5 on an iPad 2.

Any ideas?

Thanks!

agilethomas commented 7 years ago

Some more details after spending a day on it...

Here's my code for downloading the PDF:

window.requestFileSystem(window.TEMPORARY, 5 * 1024 * 1024, function (fs) {
    fs.root.getFile('test.pdf', { create: true, exclusive: false }, function (fileEntry) {
        console.log(fileEntry.toURL());
        // file:///var/mobile/Containers/Data/Application/..../tmp/test.pdf

        fileTransfer.download(uri, fileEntry.toURL(), function(entry) {
            var localUrl = entry.toInternalURL();
            console.log(localUrl);  // cdvfile://localhost/temporary/test.pdf

            // open the file here
        },
        function(error) {
            console.log("download error source " + error.source);
            console.log("download error target " + error.target);
            console.log("download error code" + error.code);
        },
        false,
        { });
    }, function() {
        console.log("onErrorCreateFile");
    });
}, function() {
    console.log("onErrorLoadFs");
});

In the "open the file here" section above, I've tried these (this first 2 window.open calls both work with either toURL or toInternalURL, and fileopener2.open works with toURL but not toInternalURL)

// this works:
window.open(entry.toURL(), '_blank',
     'location=no,closebuttoncaption=Close,enableViewportScale=yes');
// this also works:
window.open(localUrl, '_blank', 'location=no,closebuttoncaption=Close,enableViewportScale=yes');
//cordova.plugins.fileOpener2.open( // this works
cordova.plugins.fileOpener2.showOpenWithDialog( // this does not
    //localUrl, // using localUrl prints this error: 
                // Error status: 9 - Error message: File does not exist
    entry.toURL(), // this calls the success callback and the log shows 
                   //"file opened successfully" but nothing occurs on the UI
    'application/pdf', 
    { 
        error : function(e) { 
            console.log('Error status: ' + e.status + ' - Error message: ' + e.message);
        },
        success : function () {
            console.log('file opened successfully');                
        }
    }
);
pwlin commented 7 years ago

I am sorry, I don't have an iDevice at the moment to test. The iOS code was generously contributed by @stalniy

Try to message him and ask maybe he can help you.

stalniy commented 7 years ago

First of all, you need to pass real fs url (not cdv://). Second, open with dialog will not be shown if you don't have an app which can open such file. Third, it most likely will not work on emulator because emulator doesn't have apps which can open pdf

pwlin commented 7 years ago

thanks for replying @stalniy!

agilethomas commented 7 years ago

Thanks @stalniy -- Yes I was able to figure out the real fs url and not cdv:// (README needs to be updated). I included both to show the behavior that I experience when trying different things.

I am also not using an emulator. I have an iPad 2 running iOS 9.3.5 and it has Adobe Reader, so it should be able to open the PDF. I also tried today on an iPad Air running iOS 10.3.2. Neither device works calling showOpenWithDialog. If I call open() and view the document, I can then click the arrow in the upper right corner and open it in Adobe Reader.

One other interesting scenario I discovered... if I execute my function twice (by tapping my download button twice), and THEN rotate the iPad, the pop up shows in the bottom right of the screen. From there, if I click the Adobe Reader app then my app crashes.

stalniy commented 7 years ago

I will double check later as currently don't have MacBook. I tested this functionality on iphone5 and iphone6s it was working fine.

One more suggestion. Do not use tmp fs.

download() {
  const url = 'http://www.example.com/file.pdf';
  fileTransfer.download(url, this.file.dataDirectory + 'file.pdf').then((entry) => {
    console.log('download complete: ' + entry.toURL());
  }, (error) => {
    // handle error
  });
}

This is an example from ionic native docs. If you don't use ionic, replace this.file.dataDirectory with cordova.file.dataDirectory (you need to install Cordova file plugin).

agilethomas commented 7 years ago

Thanks for the help, I really appreciate it.

I tried changing the location of the file and it did not have any effect. I had been using examples from the Cordova documentation and didn't realize I could download a file by specifying cordova.file.dataDirectory so that's nice to know in any case.

As an experiment, I changed our app's target device family to include iPhone, and installed the app on my iPhone 5 (iOS 10.2.1). The call to showOpenWithDialog worked on my iPhone and I was able to open the file in Adobe Reader without a problem.

So my guess at this point is that it's related to where the dialog box is rendered on screen and for iPads it is rendered off screen (which could explain why I see the dialog box randomly when I download it twice and rotate the iPad).

agilethomas commented 7 years ago

I ran the app on my iPad with it plugged into my Mac to see the logs in the Xcode debugger. Here's what it printed (6 variations of this message):

2017-07-01 12:37:34.892 AgileLaw 12.3[1058:178860] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<NSLayoutConstraint:0x197708f0 H:[UIView:0x17d048c0(402)]>",
    "<NSLayoutConstraint:0x17e9d7f0 _UIAlertControllerView:0x17d04df0.width >= UIView:0x17d048c0.width>",
    "<NSLayoutConstraint:0x1d06aca0 _UIAlertControllerView:0x17d04df0.width == UIView:0x17ea5380.width>",
    "<NSLayoutConstraint:0x17d07cf0 'UIView-Encapsulated-Layout-Width' H:[UIView:0x17ea5380(0)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x197708f0 H:[UIView:0x17d048c0(402)]>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

Also I switched back to Cordova 6.5.0 since 7.0.1 was just released and hasn't been tested in the wild as much.

andycaoz4 commented 7 years ago

@agilethomas , did you have any luck with this? This seems to be the exact issue I'm having currently... console says file opened successful, but no openwithdialog appears. standard open works fine.. However, opening twice and changing orientation on my ipad mini does nothing, and I don't have an iphone to test whether it shows on there.

agilethomas commented 7 years ago

@andycaoz4 Yes - we did get it working. We plan to create a pull request for our fix but until then you can try out our fork: https://github.com/agilethomas/cordova-plugin-file-opener2

andycaoz4 commented 7 years ago

@agilethomas , got your branch and got everything compiled, but still no go.

I see the comments "use options.rect paramenter to FileOpener2.open...". does this mean we're extending the .open() method and we're passing options.rect into open() as parameter in js instead of using showOpenWithDialog?

Can you post up the correct js call you ended up doing for the openwith dialog box to work? Thanks.

agilethomas commented 7 years ago

We did add a parameter in the JS call so we can position the openWith dialog box. Here's our JS call:

var rect =  [0, 0, innerWidth / 2, 0];
cordova.plugins.fileOpener2.showOpenWithDialog(
    decodeURIComponent(entry.toURL()),
        'application/pdf',
        {
            error : function(e) {
                console.log('Error opening file: ' + e.status + ' - Error message: ' + e.message);
            },
            success : function (e) {
                log("success opening file");
            },
            rect: rect,
        }
)
andycaoz4 commented 7 years ago

@agilethomas YES! Thank you for this snippet! It is now working on iOS iPad Mini for me. Please enjoy an imaginary beer from me to you!

fpoirier1 commented 7 years ago

@agilethomas Any idea why the preview dialog doesn't appear on iOS 10.3 but works fine on iPad iOS 9 and 11 ..?

@andycaoz4 doest it works fine for you on iPad iOS 11

agilethomas commented 7 years ago

@fpoirier1 I don't know... pretty sure it worked on iOS 10.x but I've since upgraded to iOS 11 so I don't have an iPad to test it on right now. Will have to find one to test on now though.

sfrimont commented 6 years ago

I have this problem with an iPad Pro (ios 11.3). showOpenWithDialog is not working, however on my iphone everything works as it should. The Code-Snippet from @agilethomas makes no difference. It seems that the dialog opens somewhere you can't see it.

dmitry-grasevich commented 6 years ago

@sfrimont I have the same problem with iPad Pro (ios 11.0.2)

darylldawn commented 6 years ago

Hi. I have been using this plugin for a couple of years already, and suddenly it stopped working. I kept getting a "File does not exists" error. After hours of trying to figure out what happened, it seemed like I had to use the decodeURIComponent() method. I think it had something to do with my app name. We recently added a space character.

Anyway, after doing so the error has disappeared. I have confirmed with an iPhone and the dialog does pop up. On the iPad however, the dialog window is nowhere to be found.

Though the workaround was forked by @agilethomas, is a PR still possible at this time? It would really be a big help.

Thanks!

darylldawn commented 6 years ago

Oh, I take it back. The dialog does pop up, but after selecting "Copy to Adobe Acrobat", nothing happens. "file opened successfully" console logs but after that nothing.

Current specs iOS 11.2 Cordova 8.0


Just upgraded to iOS 11.3. I am not so familiar with native programming, but after upgrading and downgrading the plugin, I am now leaning towards the idea that this may have been caused by some access permissions of the latest iOS version.


New update (as of 2018/5/8):

After using @agilethomas's fork, the dialog pops out. (Again, thank you. I just used CGRect rect = CGRectMake(0.0, 0.0, 0.0, 0.0) just to be sure). The problem after that was whenever I chose an application outside iOS native apps (mail, notes, etc.), nothing happens. After trying to implement several "presentOpenInMenuFromRect" related functions, I came across a post about UIDocumentInteractionController and how a local document (eg. within the asset folder) that is passed to a third-party application wasn't sometimes allocated property. (I am not quite familiar with these concepts though as I have no background in Objective-C.).

So I just did the same approach that was done for Android: Copy the file to a temporary folder (eg. cordova.file.tempDirectory) and pass that file path to the showOpenWithDialog function.

I am not sure if everyone else had a similar root problem, but I just wished that I have thought of implementing the Android way with iOS in the first place and saving myself a couple of days worth of time.

Not so clean code:

checkIfFileExists(cordova.file.applicationDirectory + assetFolderFilePath);

function checkIfFileExists(path) {
    window.resolveLocalFileSystemURL(path, function (fileEntry) {
        window.resolveLocalFileSystemURL(cordova.file.tempDirectory, function (dirEntry) {
            dirEntry.getFile(fileEntry.name, {create : false}, function() {
                openFile(fileEntry.nativeURL);
            }, function() {
                fileEntry.copyTo(dirEntry, fileEntry.name, function (newFileEntry) {
                    openFile(newFileEntry.nativeURL);
                });
            });

        });
    }, getFSFail);
}

function openFile(file) {
    console.log("Opening fileopener...", file);
    cordova.plugins.fileOpener2.showOpenWithDialog(file, 'application/pdf', {
        error: function (e) {
            console.log("file did not open");
            console.log(file);
            console.log(e);
            console.log(e.message);
        },
        success: function () {
            console.log("file opened successfully");
        }
    });
}
shnist commented 5 years ago

hi @agilethomas, thanks for raising this issue. There is currently another open PR which I believe was trying to address the same problem: https://github.com/pwlin/cordova-plugin-file-opener2/pull/72. If this was merged would it fix your issue too?

shnist commented 5 years ago

closing this as a duplicate of https://github.com/pwlin/cordova-plugin-file-opener2/issues/67. There will be another release in the near future with a feature to position the dialog for iOS