Search Unity

Open PDF in Android (API level 26)

Discussion in 'Android' started by ZeFirestarter, May 16, 2019.

  1. ZeFirestarter

    ZeFirestarter

    Joined:
    Oct 24, 2014
    Posts:
    17
    Hello Forum.

    To open a PDF file in Android it used to be very easy and straight-forward.
    You were able to use "Application.OpenURL(path/filename.pdf)"and it would open the PDF with a PDF reader and by clicking "back" you went back to your application.

    Now they have changed everything.
    In the Unity documentation, it says:
    "Due security changes in Android 7.0, Application.OpenURL can no longer be used for opening local app files, you need to use FileProvider which allows you to share files with other applications."

    The problem is that the FileProvider documentation is incomplete for Unity developers.
    I tried my best to follow the instructions, but it never says which commands I would have to use in Unity.
    Also when I modify the android-manifest with the xml for the paths and the <provider> tag the app starts crashing.
    I have found other people asking the same but just no answer.

    Can anybody help?
     
  2. DeanGiddy

    DeanGiddy

    Joined:
    Oct 30, 2018
    Posts:
    10
    need an update on this if anyone has one
     
  3. blackfritz

    blackfritz

    Joined:
    Sep 13, 2018
    Posts:
    9
    Up this, we really need this in documentation
     
  4. mesengun

    mesengun

    Joined:
    Jan 20, 2015
    Posts:
    6
    Still need documantation
     
  5. NickR2600

    NickR2600

    Joined:
    Aug 17, 2018
    Posts:
    20
    Last edited: Oct 15, 2019
  6. NickR2600

    NickR2600

    Joined:
    Aug 17, 2018
    Posts:
    20
    I haven't resolved the crashing either. I get this message:

    java.lang.RuntimeException: Unable to get provider android.support.v4.content.FileProvider: java.lang.ClassNotFoundException: Didn't find class "android.support.v4.content.FileProvider" on path: DexPathList[[zip file "/data/app/com.<companyName>.<AppName>-6icxYn1Jewj1zjlBbr2ZiA==/base.apk"],nativeLibraryDirectories=[/data/app/com.<companyName>.<AppName>-6icxYn1Jewj1zjlBbr2ZiA==/lib/x86, /data/app/com.<companyName>.<AppName>-6icxYn1Jewj1zjlBbr2ZiA==/base.apk!/lib/x86, /system/lib]]

    Any advice? I'll keep looking...
     
  7. NickR2600

    NickR2600

    Joined:
    Aug 17, 2018
    Posts:
    20
    I think I've stopped the application from crashing on open. It's working on the Android emulator at least. The trick was to copy AppData\Local\Android\Sdk\extras\android\m2repository\com\android\support\support-v4\24.1.1\support-v4-24.1.1.aar to my Unity project's folder Assets/Plugins/Android. That's annoying. Shouldn't resources like this have been detected and bundled automatically? It's outline here on stackoverflow: https://stackoverflow.com/questions...e-content-fileprovider-classnotfoundexception
     
    Last edited: Oct 17, 2019
    ZeFirestarter likes this.
  8. kaarloew

    kaarloew

    Joined:
    Nov 1, 2018
    Posts:
    360
    Unity does not add support libs automatically. There is a play service resolver that solve some issues, but it also causes some.
     
  9. NickR2600

    NickR2600

    Joined:
    Aug 17, 2018
    Posts:
    20
    Good to know.

    So I'm getting closer. When the user clicks a button, the program attempts to open a PDF. But it still doesn't actually open. Instead, the Android virtual navigation buttons appear, then disappear a second later. I suspect it fails because it's not looking in the proper location. This behavior occurs regardless of trying to open a real PDF, or PDF with a bogus name that I know doesn't exist.

    Success is printed, which means the catch block never executes. I also print the URI, which reads: content://com.company.application.fileprovider/PDFs/Android/data/com.company.application/files/PDFs/Example.pdf The duplication makes me think I'm botching the URI assembly. Weird thing though, even if I create those additional folders and copy/paste the PDF there, it still doesn't work.

    The code looks something like this. The variable savePath is defined earlier using Application.persistentDataPath. Could someone please help me figure out what's going on?

    Code (CSharp):
    1.          
    2. try {
    3.     AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
    4.     AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
    5.     AndroidJavaObject unityContext = currentActivity.Call<AndroidJavaObject>("getApplicationContext");
    6.  
    7.     string packageName = unityContext.Call<string>("getPackageName");
    8.     string authority = packageName + ".fileprovider";
    9.  
    10.     AndroidJavaClass intentObj = new AndroidJavaClass("android.content.Intent");
    11.     string ACTION_VIEW = intentObj.GetStatic<string>("ACTION_VIEW");
    12.     AndroidJavaObject intent = new AndroidJavaObject("android.content.Intent", ACTION_VIEW);
    13.  
    14.     int FLAG_ACTIVITY_NEW_TASK = intentObj.GetStatic<int>("FLAG_ACTIVITY_NEW_TASK");
    15.     int FLAG_GRANT_READ_URI_PERMISSION = intentObj.GetStatic<int>("FLAG_GRANT_READ_URI_PERMISSION");
    16.              
    17.     AndroidJavaObject fileObj = new AndroidJavaObject("java.io.File", savePath);
    18.     AndroidJavaClass fileProvider = new AndroidJavaClass("android.support.v4.content.FileProvider");
    19.     AndroidJavaObject uri = fileProvider.CallStatic<AndroidJavaObject>("getUriForFile", unityContext, authority, fileObj);
    20.  
    21.     print(uri.Call<string>("toString"));
    22.  
    23.     intent.Call<AndroidJavaObject>("setDataAndType", uri, "application/vnd.android.package-archive");
    24.     intent.Call<AndroidJavaObject>("addFlags", FLAG_ACTIVITY_NEW_TASK);
    25.     intent.Call<AndroidJavaObject>("addFlags", FLAG_GRANT_READ_URI_PERMISSION);
    26.     currentActivity.Call("startActivity", intent);
    27.  
    28.     print("Success");
    29. }
    30. catch (System.Exception e) {
    31.     print("Error: " + e.Message);
    32. }
    This is the XML for the paths:
    Code (CSharp):
    1. <?xml version="1.0" encoding="utf-8"?>
    2. <paths xmlns:android="http://schemas.android.com/apk/res/android">
    3.  
    4.    <!-- files-path name="PDFs" path="."  /-->
    5.     <external-path name="PDFs" path="." />
    6.  
    7. </paths>
    Trying files-path didn't work. It resulted in java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/com.company.application/files/PDFs/Example.pdf
     
    Last edited: Oct 22, 2019
    ZeFirestarter likes this.
  10. NickR2600

    NickR2600

    Joined:
    Aug 17, 2018
    Posts:
    20
    Solved! There are a few more changes I need to make for my own code. But basically, you need to change line 23 from application/vnd.android.package-archive to application/pdf

    That was quite the time-sync. It's not nearly as developer-friendly as simply calling Application.OpenURL().
     
    ZeFirestarter likes this.
  11. seyit25

    seyit25

    Joined:
    Sep 28, 2018
    Posts:
    13
    ok but i dont understand.
    how to open pdf file?
    which command?
     
  12. NickR2600

    NickR2600

    Joined:
    Aug 17, 2018
    Posts:
    20
    I don't think it can be done with a single command anymore. Instead, it seems to require many lines that prepare and assemble several objects that culminate in a PDF finally opening.
     
  13. seyit25

    seyit25

    Joined:
    Sep 28, 2018
    Posts:
    13
    THIS IS SOLUTION
    Application.OpenURL(Application.persistentDataPath + "/" + filename); ISNOT WORKING
    AndroidContentOpenerWrapper.OpenContent(Application.persistentDataPath + "/" + filename); WORKING
     
    Hsbuha, UnetDev and ZeFirestarter like this.
  14. UnetDev

    UnetDev

    Joined:
    Aug 28, 2017
    Posts:
    54
    If your app target level API 24 or more, you need to use FileProvide API, otherwise you get FileUriExposedException, as described here.

    To interact with FileProvider from С# code, you need to know how to interact with AndroidJavaClass and AndroidJavaObject to work with Java libraries (namely android.support.v4). Also you need to know how to declares a content provider component by adding the
    <provider>
    attribute to your AndroidManifest.xml in your project and some more points that are listed here.

    In the answer of this post you can find a solution to the problem, but stumble upon the following troubles:

    1) The android.support.v4 library is no longer available on the path "AndroidSDK/extras/android/support/v4/android-support-v4.jar", since now it is part of another library.

    2) You can find this library in some archives on the Internet, but when you download them you will find that the res folder in Plugins/Android is no longer supported and you need to create a .aar binary file in which you need to put AndroidManifest.xml and android.support.v4 library.

    3) When specifying the attribute
    <provider>
    in AndroidManifest.xml, you need to additionally specify
    <meta-data>
    in which to specify the path, where in your .aar archive the provider_paths.xml file is located, otherwise your application will crash at startup.

    You can try to go this long way yourself, or download my asset, which already has all the necessary code for working with the android.support.v4 library via
    AndroidJavaClass
    and
    AndroidJavaObject
    , and which is also able to rewrite package name in AndroidManifest.xml and providers_paths.xml when you will change it in the project settings. A demo is also included there.

    Link on github: https://github.com/Mihail5412/Unity-Android-Files-Opener
     
    Last edited: Jan 18, 2020
  15. Kerihobo

    Kerihobo

    Joined:
    Jun 26, 2013
    Posts:
    65
    Don't suppose there is a clear guide out there for people who want to open a PDF on Android and iOS, but only know how to use Unity, and don't know how to build some Android plugin from Android Studio?
     
  16. Voxel-Busters

    Voxel-Busters

    Joined:
    Feb 25, 2015
    Posts:
    1,966
    It's possible with a webview! Just load the pdf url and you can have the pdf within your app.

    I understand this solution could be a overkill but in-case if you are already having a webview in pace for your TnC or login registrations, this will be handy!
     
  17. Durium

    Durium

    Joined:
    Feb 1, 2020
    Posts:
    37
    Any solution to this issue? I'm trying to downgrade my sdk but it's annoying
     
  18. Durium

    Durium

    Joined:
    Feb 1, 2020
    Posts:
    37
    Sdk downgraded and pdf opens with OpenURL but it closes down immidiately.. :(