How to share a PDF file using intents?
-
Hi, currently I am using this intent definition to share a text line from a Qt basic app:
public static void shareText(String text) { if (QtNative.activity() == null) return; Intent intent = new Intent(); intent.setAction(Intent.ACTION_SEND); sendIntent.putExtra(Intent.EXTRA_TEXT, text); sendIntent.setType("text/plain"); QtNative.activity().startActivity(intent); }
It works perfectly. Now I have to share PDF files too, so I tried this approach but it's failing:
public static void sharePDF(String path) { if (QtNative.activity() == null) return; File pdf = new File(path); if (pdf.exists()) { Intent intent = new Intent(); intent.setAction(Intent.ACTION_SEND); intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(pdf)); intent.setType("application/pdf"); QtNative.activity().startActivity(intent); } else { Log.i("DEBUG", "File doesn't exist"); } }
Any suggestion? Thanks.
-
Hi, currently I am using this intent definition to share a text line from a Qt basic app:
public static void shareText(String text) { if (QtNative.activity() == null) return; Intent intent = new Intent(); intent.setAction(Intent.ACTION_SEND); sendIntent.putExtra(Intent.EXTRA_TEXT, text); sendIntent.setType("text/plain"); QtNative.activity().startActivity(intent); }
It works perfectly. Now I have to share PDF files too, so I tried this approach but it's failing:
public static void sharePDF(String path) { if (QtNative.activity() == null) return; File pdf = new File(path); if (pdf.exists()) { Intent intent = new Intent(); intent.setAction(Intent.ACTION_SEND); intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(pdf)); intent.setType("application/pdf"); QtNative.activity().startActivity(intent); } else { Log.i("DEBUG", "File doesn't exist"); } }
Any suggestion? Thanks.
@xtingray I just went thru this story ;-)
three weeks ago I started the same way as you.
and now you're in luck: wait 1-2 days and my Share Example App for Android / iOS will be public - also corresponding blog.
Then you can View / Edit / Send to / Print any MimeType - not only PDF :) -
@xtingray I just went thru this story ;-)
three weeks ago I started the same way as you.
and now you're in luck: wait 1-2 days and my Share Example App for Android / iOS will be public - also corresponding blog.
Then you can View / Edit / Send to / Print any MimeType - not only PDF :)@ekkescorner Thank you, I will be waiting for your post! :)
-
@ekkescorner Thank you, I will be waiting for your post! :)
@xtingray I'm working on it ... nearly finished. it's a rather complicate story ;-)
-
@ekkescorner Thank you, I will be waiting for your post! :)
@xtingray I know you're waiting ;-)
so here's the repo of the example app:
https://github.com/ekke/ekkesSHAREexamplePLEASE NO QUESTIONS BEFORE MY BLOG IS PUBLISHED
I'm working on documentation and blog article to explain the workflows. stay tuned
-
@xtingray I know you're waiting ;-)
so here's the repo of the example app:
https://github.com/ekke/ekkesSHAREexamplePLEASE NO QUESTIONS BEFORE MY BLOG IS PUBLISHED
I'm working on documentation and blog article to explain the workflows. stay tuned
@ekkescorner It works! It works like a charm! I owe you a beer! :D
Thank you :) -
@ekkescorner It works! It works like a charm! I owe you a beer! :D
Thank you :)@xtingray great to hear. that it works and the beer :)
...working on my blog... -
and here's the Blog: http://blog.qt.io/blog/2017/12/01/sharing-files-android-ios-qt-app/
thx to all helping me with this -
Great stuff, thank you Ekke!
-
Hi Ekke,
Using your implementation I am having an issue with an specific version of Android (7.0), running from a tablet Samsung Galaxy Tab A sm-P580.It seems that this path is not available or accessible from my Qt app:
QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation).value(0);
As this is the path where I do the copy of the asset to share, I can't accomplish the action. Any suggestion?
PS: Quoting from -> https://developer.android.com/training/secure-file-sharing/index.html
"In all cases, the only secure way to offer a file from your app to another app is to send the receiving app the file's content URI and grant temporary access permissions to that URI. Content URIs with temporary URI access permissions are secure because they apply only to the app that receives the URI, and they expire automatically. The Android FileProvider component provides the method getUriForFile() for generating a file's content URI. " -
Hi Ekke,
Using your implementation I am having an issue with an specific version of Android (7.0), running from a tablet Samsung Galaxy Tab A sm-P580.It seems that this path is not available or accessible from my Qt app:
QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation).value(0);
As this is the path where I do the copy of the asset to share, I can't accomplish the action. Any suggestion?
PS: Quoting from -> https://developer.android.com/training/secure-file-sharing/index.html
"In all cases, the only secure way to offer a file from your app to another app is to send the receiving app the file's content URI and grant temporary access permissions to that URI. Content URIs with temporary URI access permissions are secure because they apply only to the app that receives the URI, and they expire automatically. The Android FileProvider component provides the method getUriForFile() for generating a file's content URI. "@xtingray have you set the permissions of the app ?
also please note (see readme) that current implementation is for Target SDK 23.
Google has changed the way Intents deal with URI startinmg with SDK 24, where you'll need a Content URI and a FileProvider.I started my app with the 'old' way and wanted to get this work before implementing the FileProvider.
Will do this next 2-3 weeks.BTW: my current implementation runs well on Android 7 and Android 8 devices using Target SDK 23
-
@xtingray have you set the permissions of the app ?
also please note (see readme) that current implementation is for Target SDK 23.
Google has changed the way Intents deal with URI startinmg with SDK 24, where you'll need a Content URI and a FileProvider.I started my app with the 'old' way and wanted to get this work before implementing the FileProvider.
Will do this next 2-3 weeks.BTW: my current implementation runs well on Android 7 and Android 8 devices using Target SDK 23
@ekkescorner Damn it Ekker! You saved my life twice in less than a week. Thank you again.
PS: Please, don't forget to leave a note in this thread when you update your blog post ;)
-
Yesterday I started my own expedition to implement the share file feature from Android 7.0 Nougat., following the documentation from:
https://developer.android.com/training/secure-file-sharing/index.htmlHere is a little report about my advance:
- The first challenge I had to face was to include the Java library required to call the FileProvider class from my intent implementation. I was using Qt Creator 5.8 and I couldn't fix it from there, so I updated to 5.9 and then it was really easy. All I had to do was to edit the file: android/build.gradle
And to add the next line within the dependencies section:
compile 'com.android.support:support-v4:25.0.0'
- I had to edit the Android config file android/AndroidManifest.xml to add the Provider section inside the <application> tag:
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.company.myapp.fileprovider" android:grantUriPermissions="true" android:exported="false"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths" /> </provider>
- I had to add an xml file to define a valid folder to locate all the files I want to share. Tip: If your files are not placed in that path, you will get an exception from Android 7. The xml must be created in the path android/res/xml/filepaths.xml and it must contain these lines:
<paths> <files-path path="my_inner_dir/" name="mydir" /> </paths>
Note: This directory (my_inner_dir) will be located at the "files" folder from your Android app installation path, so don't forget to create it in some point.
4. Inside my Intents section of the Java code, I added this new piece of code:import android.support.v4.content.FileProvider; ... public static void shareFile(String path) { String TAG = "Debug"; if (QtNative.activity() == null) return; File file = new File(path); if (file.exists()) { Uri fileUri; try { fileUri = FileProvider.getUriForFile(QtNative.activity(), "com.company.myapp.fileprovider", file); Log.i("Uri: ", fileUri.toString()); } catch (IllegalArgumentException e) { Log.e(TAG, "The selected file can't be shared: " + path); return; } Intent myIntent = new Intent(); myIntent.setAction(Intent.ACTION_SEND); myIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); myIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); myIntent.setDataAndType(fileUri, QtNative.activity().getContentResolver().getType(fileUri)); if (myIntent.resolveActivity(QtNative.activity().getPackageManager()) != null) QtNative.activity().startActivity(myIntent); else Log.i(TAG, "Fatal Error: Intent not resolved!"); } else { Log.i(TAG, "Fatal Error: File doesn't exist -> " + path); } }
Now, here is the far I could go: The compilation is clean and I can try to share files from my app.
What is my new issue: when I get the list of apps to share and I pick any of them, they allow me to choose the contact I want to send the file, but when I select it, nothing happens. The only app that is actually sharing the file with absolutely no issues is Google Hangout.So, the million dollar question is: What I am missing?
PS: From the console, I can see this lines when I try to share something from WhatsApp, Telegram or any other app (except GH):
I/Uri: ( 2869): content://com.company.myapp..fileprovider/mydir/test.png I/ActivityManager( 945): START u0 {act=android.intent.action.SEND dat=content://com.company.myapp.fileprovider/mydir/test.png typ=image/png flg=0x3 cmp=android/com.android.internal.app.ResolverActivity} from pid 2869 V/HardwareRenderer( 2869): Gl20Renderer.startTrimMemory: not force render mem full trim. W/dalvikvm( 2645): VFY: unable to resolve virtual method 12869: Landroid/widget/ImageView;.setImageIcon (Landroid/graphics/drawable/Icon;)V
- The first challenge I had to face was to include the Java library required to call the FileProvider class from my intent implementation. I was using Qt Creator 5.8 and I couldn't fix it from there, so I updated to 5.9 and then it was really easy. All I had to do was to edit the file: android/build.gradle
-
Yesterday I started my own expedition to implement the share file feature from Android 7.0 Nougat., following the documentation from:
https://developer.android.com/training/secure-file-sharing/index.htmlHere is a little report about my advance:
- The first challenge I had to face was to include the Java library required to call the FileProvider class from my intent implementation. I was using Qt Creator 5.8 and I couldn't fix it from there, so I updated to 5.9 and then it was really easy. All I had to do was to edit the file: android/build.gradle
And to add the next line within the dependencies section:
compile 'com.android.support:support-v4:25.0.0'
- I had to edit the Android config file android/AndroidManifest.xml to add the Provider section inside the <application> tag:
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.company.myapp.fileprovider" android:grantUriPermissions="true" android:exported="false"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths" /> </provider>
- I had to add an xml file to define a valid folder to locate all the files I want to share. Tip: If your files are not placed in that path, you will get an exception from Android 7. The xml must be created in the path android/res/xml/filepaths.xml and it must contain these lines:
<paths> <files-path path="my_inner_dir/" name="mydir" /> </paths>
Note: This directory (my_inner_dir) will be located at the "files" folder from your Android app installation path, so don't forget to create it in some point.
4. Inside my Intents section of the Java code, I added this new piece of code:import android.support.v4.content.FileProvider; ... public static void shareFile(String path) { String TAG = "Debug"; if (QtNative.activity() == null) return; File file = new File(path); if (file.exists()) { Uri fileUri; try { fileUri = FileProvider.getUriForFile(QtNative.activity(), "com.company.myapp.fileprovider", file); Log.i("Uri: ", fileUri.toString()); } catch (IllegalArgumentException e) { Log.e(TAG, "The selected file can't be shared: " + path); return; } Intent myIntent = new Intent(); myIntent.setAction(Intent.ACTION_SEND); myIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); myIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); myIntent.setDataAndType(fileUri, QtNative.activity().getContentResolver().getType(fileUri)); if (myIntent.resolveActivity(QtNative.activity().getPackageManager()) != null) QtNative.activity().startActivity(myIntent); else Log.i(TAG, "Fatal Error: Intent not resolved!"); } else { Log.i(TAG, "Fatal Error: File doesn't exist -> " + path); } }
Now, here is the far I could go: The compilation is clean and I can try to share files from my app.
What is my new issue: when I get the list of apps to share and I pick any of them, they allow me to choose the contact I want to send the file, but when I select it, nothing happens. The only app that is actually sharing the file with absolutely no issues is Google Hangout.So, the million dollar question is: What I am missing?
PS: From the console, I can see this lines when I try to share something from WhatsApp, Telegram or any other app (except GH):
I/Uri: ( 2869): content://com.company.myapp..fileprovider/mydir/test.png I/ActivityManager( 945): START u0 {act=android.intent.action.SEND dat=content://com.company.myapp.fileprovider/mydir/test.png typ=image/png flg=0x3 cmp=android/com.android.internal.app.ResolverActivity} from pid 2869 V/HardwareRenderer( 2869): Gl20Renderer.startTrimMemory: not force render mem full trim. W/dalvikvm( 2645): VFY: unable to resolve virtual method 12869: Landroid/widget/ImageView;.setImageIcon (Landroid/graphics/drawable/Icon;)V
@xtingray thx providing your experiences.
should be easier to add the android support library ;-)will take some weeks until I can continue and add target sdk >23 file sharing support using FileProvider.
next week I'll add support the other way: HowTo provide the App to others as share target - there are also some traps HowTo deal with the multi instances of the app on Android and I have to figure out HowTo do it from iOS.
- The first challenge I had to face was to include the Java library required to call the FileProvider class from my intent implementation. I was using Qt Creator 5.8 and I couldn't fix it from there, so I updated to 5.9 and then it was really easy. All I had to do was to edit the file: android/build.gradle
-
Yesterday I started my own expedition to implement the share file feature from Android 7.0 Nougat., following the documentation from:
https://developer.android.com/training/secure-file-sharing/index.htmlHere is a little report about my advance:
- The first challenge I had to face was to include the Java library required to call the FileProvider class from my intent implementation. I was using Qt Creator 5.8 and I couldn't fix it from there, so I updated to 5.9 and then it was really easy. All I had to do was to edit the file: android/build.gradle
And to add the next line within the dependencies section:
compile 'com.android.support:support-v4:25.0.0'
- I had to edit the Android config file android/AndroidManifest.xml to add the Provider section inside the <application> tag:
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.company.myapp.fileprovider" android:grantUriPermissions="true" android:exported="false"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths" /> </provider>
- I had to add an xml file to define a valid folder to locate all the files I want to share. Tip: If your files are not placed in that path, you will get an exception from Android 7. The xml must be created in the path android/res/xml/filepaths.xml and it must contain these lines:
<paths> <files-path path="my_inner_dir/" name="mydir" /> </paths>
Note: This directory (my_inner_dir) will be located at the "files" folder from your Android app installation path, so don't forget to create it in some point.
4. Inside my Intents section of the Java code, I added this new piece of code:import android.support.v4.content.FileProvider; ... public static void shareFile(String path) { String TAG = "Debug"; if (QtNative.activity() == null) return; File file = new File(path); if (file.exists()) { Uri fileUri; try { fileUri = FileProvider.getUriForFile(QtNative.activity(), "com.company.myapp.fileprovider", file); Log.i("Uri: ", fileUri.toString()); } catch (IllegalArgumentException e) { Log.e(TAG, "The selected file can't be shared: " + path); return; } Intent myIntent = new Intent(); myIntent.setAction(Intent.ACTION_SEND); myIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); myIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); myIntent.setDataAndType(fileUri, QtNative.activity().getContentResolver().getType(fileUri)); if (myIntent.resolveActivity(QtNative.activity().getPackageManager()) != null) QtNative.activity().startActivity(myIntent); else Log.i(TAG, "Fatal Error: Intent not resolved!"); } else { Log.i(TAG, "Fatal Error: File doesn't exist -> " + path); } }
Now, here is the far I could go: The compilation is clean and I can try to share files from my app.
What is my new issue: when I get the list of apps to share and I pick any of them, they allow me to choose the contact I want to send the file, but when I select it, nothing happens. The only app that is actually sharing the file with absolutely no issues is Google Hangout.So, the million dollar question is: What I am missing?
PS: From the console, I can see this lines when I try to share something from WhatsApp, Telegram or any other app (except GH):
I/Uri: ( 2869): content://com.company.myapp..fileprovider/mydir/test.png I/ActivityManager( 945): START u0 {act=android.intent.action.SEND dat=content://com.company.myapp.fileprovider/mydir/test.png typ=image/png flg=0x3 cmp=android/com.android.internal.app.ResolverActivity} from pid 2869 V/HardwareRenderer( 2869): Gl20Renderer.startTrimMemory: not force render mem full trim. W/dalvikvm( 2645): VFY: unable to resolve virtual method 12869: Landroid/widget/ImageView;.setImageIcon (Landroid/graphics/drawable/Icon;)V
@xtingray I'm just implementing the FileProvider and using content Uri for SEND, VIEW, EDIT
over all looks good - so will go public soon and Qt Blog will also followbut as always - there's also a problem
ACTION_EDIT doesn't work with Google Photos App. Getting toast message 'Editing is not supported for this image'
ACTION_VIEW works
downloaded another app (Photo Editor) from Google Play and it works for EDIT or VIEW
so my implementation seems to be okusing the old SDK23 - FilePath same images can be EDITed with Google Photos with no problems
found this: https://productforums.google.com/forum/#!msg/photos/2MGDEOUhhl0/oNJR_nROAAAJ
but wasn't really helpfulare you using EDIT together with Google Photos ?
- The first challenge I had to face was to include the Java library required to call the FileProvider class from my intent implementation. I was using Qt Creator 5.8 and I couldn't fix it from there, so I updated to 5.9 and then it was really easy. All I had to do was to edit the file: android/build.gradle