I use PdfRenderer to render PDF preview in Android API 25:
var parcelFileDescriptor: ParcelFileDescriptor? = null
var pdfRenderer: PdfRenderer? = null
var firstPage: PdfRenderer.Page? = null
try {
val file = File(filePath)
parcelFileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY)
pdfRenderer = PdfRenderer(parcelFileDescriptor) // it throw exception or crash from here
firstPage = pdfRenderer.openPage(0)
// Do something with firstPage
} catch (e: Exception) {
e.printStackTrace()
} finally {
firstPage?.close()
pdfRenderer?.close()
parcelFileDescriptor?.close()
}
But in first call, it throws java.lang.SecurityException: cannot create document. Error: 4. From 2nd or 3rd call, it is not responding and crash in native.
I just call it from only one thread, so there is no concurrency issue. Can anyone help me?
I found trying to open a password protected pdf on Android < P causes the crash: https://issuetracker.google.com/issues/37052344.
What to do is checking if the pdf file is encrypted or not before opening.
Related
I download a file using Retrofit and save it in a subfolder in the download directory.
when I check with the phone's file manager, it is downloaded and saved correctly. For example, in the following path:
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path + "/MyApp"
But when I open the file manager with intent like ACTION_GET_CONTENT or ACTION_OPEN_DOCUMENT the downloaded file is not visible.
In addition, if I rename the file or copy and paste it through the phone's file manager, everything will be fixed in the same path
Also, saving in the downloads folder is done without any problem
But when the subfolder is created and it is supposed to be saved there, this problem occurs
There is no problem with downloading by DownloadManager, but i want use retofit
Download function:
suspend fun download(url: String, targetPath: String, progressRetrofit: RetrofitProgress) = flow {
try {
val response = apiService.download(url).awaitResponse()
val body = response.body()
if (response.isSuccessful && body != null) {
try {
val file = File(targetPath)
body.byteStream().use { inputStream ->
FileOutputStream(file).use { outputStream ->
val data = ByteArray(1024)
var read: Int
var currentDownloadSize = 0L
val fileSize = body.contentLength()
while (inputStream.read(data).also { read = it } != -1) {
outputStream.write(data, 0, read)
currentDownloadSize += read
withContext(Dispatchers.Main)
{
progressRetrofit.onProgressUpdate((currentDownloadSize * 100 / fileSize).toInt(), fileSize, currentDownloadSize)
}
}
withContext(Dispatchers.Main)
{
progressRetrofit.onProgressUpdate((currentDownloadSize * 100 / fileSize).toInt(), fileSize, currentDownloadSize)
}
outputStream.close()
outputStream.flush()
}
}
emit(NetworkResult.Success(true))
} catch (e: Exception) {
emit(NetworkResult.Failure(e.message.toString()))
errorMessage(e.message.toString(), true)
}
} else {
emit(NetworkResult.Failure(response.message()))
errorMessage(response.errorBody().toString(), true)
}
} catch (e: Exception) {
emit(NetworkResult.Failure(e.message.toString()))
errorMessage(e.message.toString(), true)
}
}
Hmmmm...
You are right...
It happens.
But only if with ACTION_OPEN_DOCUMENT the user directly uses the Downloads item.
Instead the user should browse the device, and go to Download directory and then to the subdirectory.
(Note: The first ends with an s, the real directory is without s.).
After some more tests it appeared if the file was scanned by the MediaStore.
So add some few lines of code to let it be scanned after download.
Since Android 6.0 there are bunch of changes in file sharing behaviour.
Please take a look on FileProvider and look further through API changes in the official android documentation, e.g. like this.
Without extra details in your code or even reproducible code sample I can not help more.
I am writing something to read a Json in Android with Kotlin.
The Goal is to either start the ChooseLoginActicity if there is no User.Json available or to otherwise read the Json and start the MainActivity.
The problem is that the line to open the File into a Input Stream always gets an FileNotFoundException even if the User.Json exists with the Permission -rw-rw---- and got created with Context.MODE_PRIVATE, by the same Application.
This Function got called in the onCreate Function of the Activity.
fun readJsonFile() {
try {
val inputStream = openFileInput("User.json")
val json = inputStream.bufferedReader().use { it.readText() }
val gson = Gson()
val user = gson.fromJson(json, User::class.java)
CurrentUser.initUser(user.id,user.name)
startActivity(Intent(this, MainActivity::class.java))
} catch (e: FileNotFoundException) {
startActivity(Intent(this, ChooseLoginActivity::class.java))
}
}
Make sure you wrote the correct file name. Pay attention about case sensitivity.
The code itself should work fine.
I am trying to save/export a file on the user Documents shared folder so it can be persistent if the application is deleted (it's an export of the user work on the application). Following the official documentation to create a save a file on the shared folder, I have this basic implementation:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
registerActivity = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){ result ->
if (result.resultCode == Activity.RESULT_OK) {
result.data?.data?.let { writeInFile(it, "this is a test") }
}
}
}
private fun createFile() {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply{
addCategory(Intent.CATEGORY_OPENABLE)
type = "text/plain"
putExtra(Intent.EXTRA_TITLE, "filename.txt")
putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.DIRECTORY_DOCUMENTS)
}
startActivityForResult(intent, WRITE_REQUEST_CODE)
//this.registerActivity.launch(intent)
}
private fun writeInFile(uri: Uri, text: String) {
val outputStream: OutputStream
try {
Log.i("export", uri.toString())
outputStream = contentResolver.openOutputStream(uri)!!
val bw = BufferedWriter(OutputStreamWriter(outputStream))
bw.write(text)
bw.flush()
bw.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
The createFile function is called from a click event to export the data.
The Activity for choosing the folder to save is launching, but saving is failing. I am getting the following error:
2021-06-23 18:24:43.268 2853-2871/? E/DatabaseUtils: Writing exception to parcel
java.lang.IllegalArgumentException: Parent document isn't a directory
at com.android.internal.content.FileSystemProvider.createDocument(FileSystemProvider.java:244)
at com.android.providers.downloads.DownloadStorageProvider.createDocument(DownloadStorageProvider.java:207)
at android.provider.DocumentsProvider.callUnchecked(DocumentsProvider.java:1124)
at android.provider.DocumentsProvider.call(DocumentsProvider.java:1067)
at android.content.ContentProvider.call(ContentProvider.java:2448)
at android.content.ContentProvider$Transport.call(ContentProvider.java:517)
at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:295)
at android.os.Binder.execTransactInternal(Binder.java:1154)
at android.os.Binder.execTransact(Binder.java:1123)
2021-06-23 18:24:43.269 1559-1680/? W/DocumentsContract: Failed to create document
java.lang.IllegalArgumentException: Parent document isn't a directory
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:172)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:142)
at android.content.ContentProviderProxy.call(ContentProviderNative.java:732)
at android.content.ContentProviderClient.call(ContentProviderClient.java:603)
at android.content.ContentResolver.call(ContentResolver.java:2395)
at android.provider.DocumentsContract.createDocument(DocumentsContract.java:1371)
at com.android.documentsui.DocumentsAccess$RuntimeDocumentAccess.createDocument(DocumentsAccess.java:157)
at com.android.documentsui.picker.CreatePickedDocumentTask.run(CreatePickedDocumentTask.java:79)
at com.android.documentsui.picker.CreatePickedDocumentTask.run(CreatePickedDocumentTask.java:42)
at com.android.documentsui.base.CheckedTask.doInBackground(CheckedTask.java:65)
at android.os.AsyncTask$3.call(AsyncTask.java:394)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at com.android.documentsui.ProviderExecutor.run(ProviderExecutor.java:104)
I just found that the issue wasn't in my implementation, but my Android emulator. I tried to create a folder from the file manager (outside my application) and I was getting the same error. So, I tried testing my application on a physical device, it worked perfectly!
The issue with my emulator was that the SD card was corrupted (either it wasn't set up or something went wrong at some point without me realizing it). Resetting that fixed the issue
I am trying to build an app that use google speech cloud api in android kotlin
here is my code
launch {
googleTextToSpeech = TextToSpeechClient.create()
googleTextToSpeech?.let {
viewModel.speakGoogle(googleTextToSpeech!!, totalMessage, player)
}
}
fun speakGoogle(textToSpeech: com.google.cloud.texttospeech.v1.TextToSpeechClient, message: String, player: MediaPlayer) {
val filePath = Environment.getExternalStorageDirectory().absolutePath + "/google_" + System.currentTimeMillis() + ".mp3"
launch {
var msg = android.text.Html.fromHtml(message).toString()
msg = msg.replace("\"", "")
var input = SynthesisInput.newBuilder().setText(msg).build()
var voice = VoiceSelectionParams.newBuilder().setLanguageCode("en-US").setSsmlGender(SsmlVoiceGender.FEMALE).build()
var audio = AudioConfig.newBuilder().setAudioEncoding(AudioEncoding.MP3).build()
var response = textToSpeech.synthesizeSpeech(input, voice, audio)
response?.let {
try {
val inputStream = response.audioContent.toByteArray()
File(filePath).outputStream().use { inputStream }
player.setDataSource(filePath)
player.prepare()
player.start()
} catch (e: IOException) {
Log.i("AMIRA00000", e.toString())
} catch (e: IllegalStateException) {
Log.i("AMIRA00000", e.toString())
}
}
}
}
I am also having my google-service.json file included in the app
but I am getting the following error
java.io.IOException: The Application Default Credentials are not available. They are available if running in Google Compute Engine. Otherwise, the environment variable GOOGLE_APPLICATION_CREDENTIALS must be defined pointing to a file defining the credentials. See https://developers.google.com/accounts/docs/application-default-credentials for more information.
at com.google.auth.oauth2.DefaultCredentialsProvider.getDefaultCredentials(DefaultCredentialsProvider.java:134)
at com.google.auth.oauth2.GoogleCredentials.getApplicationDefault(GoogleCredentials.java:119)
at com.google.auth.oauth2.GoogleCredentials.getApplicationDefault(GoogleCredentials.java:91)
at com.google.api.gax.core.GoogleCredentialsProvider.getCredentials(GoogleCredentialsProvider.java:67)
at com.google.api.gax.rpc.ClientContext.create(ClientContext.java:135)
at com.google.cloud.texttospeech.v1.stub.GrpcTextToSpeechStub.create(GrpcTextToSpeechStub.java:74)
at com.google.cloud.texttospeech.v1.stub.TextToSpeechStubSettings.createStub(TextToSpeechStubSettings.java:100)
at com.google.cloud.texttospeech.v1.TextToSpeechClient.<init>(TextToSpeechClient.java:128)
at com.google.cloud.texttospeech.v1.TextToSpeechClient.create(TextToSpeechClient.java:109)
at com.google.cloud.texttospeech.v1.TextToSpeechClient.create(TextToSpeechClient.java:101)
at com.sbs16.ensofia.view.main.MainFragment$setupBinding$3$$special$$inlined$let$lambda$2.invokeSuspend(MainFragment.kt:186)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)
I am also having another json file thathave private key and other credential settings but I do not know how to use it
I'm not an Android developer, but my feeling is that you shouldn't call the TextToSpeech directly from your Android app. You should call it through a backend (On AppEngine for example, or on Firebase Functions).
This backend is authenticated by the TextToSpeech API, and your Android client is authenticated on your backend.
Thereby, you can control who use your app, and your TextToSpeech feature. In any case, never put the service account secret key file in your app, either anybody that download you app can steal the key and perform call on the service account behalf and you will pay the bill!
When I don't have internet on my phone or when the URL does not exist this method not doing anything neither crash nor stop searching.
I tried this :
doAsync{
val json:String
json = try {
URL("http://10.0.2.2:8888/bac/orient.php?ort=1").readText()
}catch (e: IOException){
e.printStackTrace()
""
}
uiThread{toast(json)}
}