Save and Open PDF on Android!
-
Hello.. I am saving a PDF file on the Android device as follows:
QString fileName = QFileDialog::getSaveFileName(this, tr("Exportar relatório..."), QString(), tr("PDF (*.pdf);;HTML (*.htm *.html);;Excel (*.csv);;Todos Arquivos (*)")); if(fileName.isEmpty()) return; printer->setOutputFormat(QPrinter::PdfFormat); printer->setOutputFileName(fileName); TE_Rel->document()->print(printer);
This way I use on Windows and now I'm using it on Android.
Apparently the PDF is successfully saved in the APP folder. But I can't view the APP form PDF, which I believe is related to Android permissions.Given this, I'm trying to open the PDF right after saving. I use the following code to try to open it:
QString filePath = fileName; //The file exists at that location, I used the same path to create the pdf QDesktopServices::openUrl(QUrl::fromLocalFile(filePath));
However, the PDF is not opened through a standard PDF viewer that is installed on the device.
Any other alternative to save and read PDF on Android?
I use QT 5.14.2.
Thanks. -
I will add the following permissions:
<uses-permission android:name="android.permission.ACCESS_CHECKIN_PROPERTIES"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_SETTINGS"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_PROFILE"/>
But apparently it has no effect.
Whenever I save the PDF, the QFileDialog opens, allowing saving only within the APP folder.
Any idea?
-
Hello.. Okay. Change to:
QString caminho = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); QString fileName = QFileDialog::getSaveFileName(this, tr("Exportar relatório..."), caminho, tr("PDF (*.pdf);;HTML (*.htm *.html);;Excel (*.csv);;Todos Arquivos (*)")); if(fileName.isEmpty()) return;
Now open the "Documents" folder. Saves correctly. But I can't open it with my APP through QDesktopServices
-
not familiar with QDesktopServices. What is the error message?
the following link is a bit old. But it may help a bit.
https://stackoverflow.com/questions/18786919/qt-android-qdesktopservicesopenurl-qurlfromlocalfile-pdf-with
https://bugreports.qt.io/browse/QTBUG-67877 -
W System.err: android.os.FileUriExposedException: file:///storage/emulated/0/Documents/Caduser.pdf exposed beyond app through Intent.getData()
W System.err: at android.os.StrictMode.onFileUriExposed(StrictMode.java:2209)
W System.err: at android.net.Uri.checkFileUriExposed(Uri.java:2402)
W System.err: at android.content.Intent.prepareToLeaveProcess(Intent.java:12108)
W System.err: at android.content.Intent.prepareToLeaveProcess(Intent.java:12057)
W System.err: at android.app.Instrumentation.execStartActivity(Instrumentation.java:1742)
W System.err: at android.app.Activity.startActivityForResult(Activity.java:5473)
W System.err: at android.app.Activity.startActivityForResult(Activity.java:5431)
W System.err: at android.app.Activity.startActivity(Activity.java:5817)
W System.err: at android.app.Activity.startActivity(Activity.java:5770)
W System.err: at org.qtproject.qt5.android.QtNative.openURL(QtNative.java:164)
W System.err: at org.qtproject.qt5.android.QtNative.startQtApplication(Native Method)
W System.err: at org.qtproject.qt5.android.QtNative$7.run(QtNative.java:390)
W System.err: at org.qtproject.qt5.android.QtThread$1.run(QtThread.java:61)
W System.err: at java.lang.Thread.run(Thread.java:1012) -
W System.err: android.os.FileUriExposedException: file:///storage/emulated/0/Documents/Caduser.pdf exposed beyond app through Intent.getData()
W System.err: at android.os.StrictMode.onFileUriExposed(StrictMode.java:2209)
W System.err: at android.net.Uri.checkFileUriExposed(Uri.java:2402)
W System.err: at android.content.Intent.prepareToLeaveProcess(Intent.java:12108)
W System.err: at android.content.Intent.prepareToLeaveProcess(Intent.java:12057)
W System.err: at android.app.Instrumentation.execStartActivity(Instrumentation.java:1742)
W System.err: at android.app.Activity.startActivityForResult(Activity.java:5473)
W System.err: at android.app.Activity.startActivityForResult(Activity.java:5431)
W System.err: at android.app.Activity.startActivity(Activity.java:5817)
W System.err: at android.app.Activity.startActivity(Activity.java:5770)
W System.err: at org.qtproject.qt5.android.QtNative.openURL(QtNative.java:164)
W System.err: at org.qtproject.qt5.android.QtNative.startQtApplication(Native Method)
W System.err: at org.qtproject.qt5.android.QtNative$7.run(QtNative.java:390)
W System.err: at org.qtproject.qt5.android.QtThread$1.run(QtThread.java:61)
W System.err: at java.lang.Thread.run(Thread.java:1012)@RenanHm said in Save and Open PDF on Android!:
W System.err: at java.lang.Thread.run(Thread.java:1012)
Have you checked permission on startup?
#ifdef REQUEST_PERMISSIONS_ON_ANDROID #include <QtAndroid> bool requestStoragePermission() { using namespace QtAndroid; SOPermission sop; QStringList permissions = {"android.permission.ACCESS_FINE_LOCATION"}; const QHash<QString, PermissionResult> results = requestPermissionsSync(permissions); auto ok = true; auto i = 0; while (ok && i< permissions.size()) { if (!results.contains(permissions[i]) || results[permissions[i]] == PermissionResult::Denied) { qCritical() << "Couldn't get permission: " << permissions[i]; ok = false; --i; } ++i; } return ok; } #endif
-
Hello.. I performed the test with the function mentioned. Change to permission:
QStringList permissions = {"android.permission.READ_EXTERNAL_STORAGE"};
It is returned to me that it has permission. But without any practical effect.
Searching more.. I found that in the most recent versions of Android it is now necessary to use "Provider".
https://bugreports.qt.io/browse/QTBUG-67877
https://bugreports.qt.io/browse/QTBUG-85238Following the guidelines for using the Provider, I add in the Manifest:
<provider android:name="androidx.core.content.FileProvider" android:authorities="com.example.myapp.fileprovider" android:grantUriPermissions="true" android:exported="false"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths" /> </provider>
I also include the "filepaths" file in the "xml" folder.
But the error always occurs when opening the APP:E AndroidRuntime: FATAL EXCEPTION: main E AndroidRuntime: Process: com.digitalsof.totallocapp, PID: 4718 E AndroidRuntime: java.lang.RuntimeException: Unable to get provider androidx.core.content.FileProvider: java.lang.ClassNotFoundException: Didn't find class "androidx.core.content.FileProvider" on path: DexPathList[[zip file "/data/app/~~qh7wYdLZU6AvVfgP104ckQ==/com.digitalsof.totallocapp-pzAvkKqAw1X05TWYviFvnA==/base.apk"],nativeLibraryDirectories=[/data/app/~~qh7wYdLZU6AvVfgP104ckQ==/com.digitalsof.totallocapp-pzAvkKqAw1X05TWYviFvnA==/lib/arm64, /data/app/~~qh7wYdLZU6AvVfgP104ckQ==/com.digitalsof.totallocapp-pzAvkKqAw1X05TWYviFvnA==/base.apk!/lib/arm64-v8a, /system/lib64, /system/system_ext/lib64]] E AndroidRuntime: at android.app.ActivityThread.installProvider(ActivityThread.java:8231) E AndroidRuntime: at android.app.ActivityThread.installContentProviders(ActivityThread.java:7728) E AndroidRuntime: at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7482) E AndroidRuntime: at android.app.ActivityThread.access$1600(ActivityThread.java:310) E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2281) E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:106) E AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:226) E AndroidRuntime: at android.os.Looper.loop(Looper.java:313) E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:8669) E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method) E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571) E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135) E AndroidRuntime: Caused by: java.lang.ClassNotFoundException: Didn't find class "androidx.core.content.FileProvider" on path: DexPathList[[zip file "/data/app/~~qh7wYdLZU6AvVfgP104ckQ==/com.digitalsof.totallocapp-pzAvkKqAw1X05TWYviFvnA==/base.apk"],nativeLibraryDirectories=[/data/app/~~qh7wYdLZU6AvVfgP104ckQ==/com.digitalsof.totallocapp-pzAvkKqAw1X05TWYviFvnA==/lib/arm64, /data/app/~~qh7wYdLZU6AvVfgP104ckQ==/com.digitalsof.totallocapp-pzAvkKqAw1X05TWYviFvnA==/base.apk!/lib/arm64-v8a, /system/lib64, /system/system_ext/lib64]] E AndroidRuntime: at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:259) E AndroidRuntime: at java.lang.ClassLoader.loadClass(ClassLoader.java:379) E AndroidRuntime: at java.lang.ClassLoader.loadClass(ClassLoader.java:312) E AndroidRuntime: at android.app.AppComponentFactory.instantiateProvider(AppComponentFactory.java:147) E AndroidRuntime: at android.app.ActivityThread.installProvider(ActivityThread.java:8215) E AndroidRuntime: ... 11 more
Any idea?
-
Hello.. I performed the test with the function mentioned. Change to permission:
QStringList permissions = {"android.permission.READ_EXTERNAL_STORAGE"};
It is returned to me that it has permission. But without any practical effect.
Searching more.. I found that in the most recent versions of Android it is now necessary to use "Provider".
https://bugreports.qt.io/browse/QTBUG-67877
https://bugreports.qt.io/browse/QTBUG-85238Following the guidelines for using the Provider, I add in the Manifest:
<provider android:name="androidx.core.content.FileProvider" android:authorities="com.example.myapp.fileprovider" android:grantUriPermissions="true" android:exported="false"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths" /> </provider>
I also include the "filepaths" file in the "xml" folder.
But the error always occurs when opening the APP:E AndroidRuntime: FATAL EXCEPTION: main E AndroidRuntime: Process: com.digitalsof.totallocapp, PID: 4718 E AndroidRuntime: java.lang.RuntimeException: Unable to get provider androidx.core.content.FileProvider: java.lang.ClassNotFoundException: Didn't find class "androidx.core.content.FileProvider" on path: DexPathList[[zip file "/data/app/~~qh7wYdLZU6AvVfgP104ckQ==/com.digitalsof.totallocapp-pzAvkKqAw1X05TWYviFvnA==/base.apk"],nativeLibraryDirectories=[/data/app/~~qh7wYdLZU6AvVfgP104ckQ==/com.digitalsof.totallocapp-pzAvkKqAw1X05TWYviFvnA==/lib/arm64, /data/app/~~qh7wYdLZU6AvVfgP104ckQ==/com.digitalsof.totallocapp-pzAvkKqAw1X05TWYviFvnA==/base.apk!/lib/arm64-v8a, /system/lib64, /system/system_ext/lib64]] E AndroidRuntime: at android.app.ActivityThread.installProvider(ActivityThread.java:8231) E AndroidRuntime: at android.app.ActivityThread.installContentProviders(ActivityThread.java:7728) E AndroidRuntime: at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7482) E AndroidRuntime: at android.app.ActivityThread.access$1600(ActivityThread.java:310) E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2281) E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:106) E AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:226) E AndroidRuntime: at android.os.Looper.loop(Looper.java:313) E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:8669) E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method) E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571) E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135) E AndroidRuntime: Caused by: java.lang.ClassNotFoundException: Didn't find class "androidx.core.content.FileProvider" on path: DexPathList[[zip file "/data/app/~~qh7wYdLZU6AvVfgP104ckQ==/com.digitalsof.totallocapp-pzAvkKqAw1X05TWYviFvnA==/base.apk"],nativeLibraryDirectories=[/data/app/~~qh7wYdLZU6AvVfgP104ckQ==/com.digitalsof.totallocapp-pzAvkKqAw1X05TWYviFvnA==/lib/arm64, /data/app/~~qh7wYdLZU6AvVfgP104ckQ==/com.digitalsof.totallocapp-pzAvkKqAw1X05TWYviFvnA==/base.apk!/lib/arm64-v8a, /system/lib64, /system/system_ext/lib64]] E AndroidRuntime: at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:259) E AndroidRuntime: at java.lang.ClassLoader.loadClass(ClassLoader.java:379) E AndroidRuntime: at java.lang.ClassLoader.loadClass(ClassLoader.java:312) E AndroidRuntime: at android.app.AppComponentFactory.instantiateProvider(AppComponentFactory.java:147) E AndroidRuntime: at android.app.ActivityThread.installProvider(ActivityThread.java:8215) E AndroidRuntime: ... 11 more
Any idea?
@RenanHm
In the my app I haved inserted in the all permission that we need .
const QStringList m_permissionsNameListAndroid = {"android.permission.ACCESS_FINE_LOCATION","android.permission.ACCESS_COARSE_LOCATION","android.permission.READ_EXTERNAL_STORAGE","android.permission.WRITE_EXTERNAL_STORAGE","android.permission.CHANGE_WIFI_STATE","android.permission.READ_PHONE_STATE","android.permission.ACCESS_WIFI_STATE","android.permission.BLUETOOTH","android.permission.BLUETOOTH_ADMIN","android.permission.VIBRATE","android.permission.RECEIVE_BOOT_COMPLETED"};For Android > 10 I have ceate this function to create an file on directory. So You can test, if you can write on directory. I neved opened a url but I think the way is create a function java.
For example
//https://trendoceans.com/how-to-open-pdf-programmatically-using-intent-in-android/For open the url
public static String directoryDownLoad() {
Context context = m_instance.getApplicationContext();
File path = android.os.Environment.getExternalStoragePublicDirectory(
android.os.Environment.DIRECTORY_DOWNLOADS);
Log.v(m_instance.TAG,"directoryDownLoad"+path.getAbsolutePath());
return path.getAbsolutePath();
}Id
-
I'm managing to save the PDF, as I went above.
The problem is in opening the PDF.From what I researched, the "Provider" should be created and call the native java class through "QAndroidJniObject".
But I can't move forward when I edit the Manifest file as above.
-
I'm managing to save the PDF, as I went above.
The problem is in opening the PDF.From what I researched, the "Provider" should be created and call the native java class through "QAndroidJniObject".
But I can't move forward when I edit the Manifest file as above.
-
@RenanHm
In this case usually I create a basic project with Android Studio to open a pdf. Then you can push the function in activity that you have extended in Qt for Android@piervalli said in Save and Open PDF on Android!:
In this case usually I create a basic project with Android Studio to open a pdf. Then you can push the function in activity that you have extended in Qt for Android
I understood. I am going to try..
-
I'm having the same problem with printing documents on Android... QT doesn't recognize the drivers installed... it just gives the option to save to PDF... Do you know anything about this?
-
@RenanHm
I am remember that the printer is not supported on Android, I think that you need same way.@piervalli Got it.. I'll research about it. Thanks.