kotlin androidpdfviewer lib doesn't seem to load - android

I am working with android pdfviewer lib to open and read a pdf, found at : https://github.com/barteksc/AndroidPdfViewer
But i got an error when i try to launch the pdf :
E/zygote64: No implementation found for long com.shockwave.pdfium.PdfiumCore.nativeOpenDocument(int, java.lang.String) (tried Java_com_shockwave_pdfium_PdfiumCore_nativeOpenDocument and Java_com_shockwave_pdfium_PdfiumCore_nativeOpenDocument__ILjava_lang_String_2)
E/PDFView: load pdf error
java.lang.UnsatisfiedLinkError: No implementation found for long com.shockwave.pdfium.PdfiumCore.nativeOpenDocument(int, java.lang.String) (tried Java_com_shockwave_pdfium_PdfiumCore_nativeOpenDocument and Java_com_shockwave_pdfium_PdfiumCore_nativeOpenDocument__ILjava_lang_String_2)
i tried with differents implementation of the dependency but none worked :
implementation 'com.github.barteksc:pdfium-android:1.9.0'
implementation "com.github.barteksc:android-pdf-viewer:3.2.0-beta.1"
implementation "com.github.barteksc:android-pdf-viewer:2.8.2"
The error is found here :
public PdfDocument newDocument(ParcelFileDescriptor fd, String password) throws IOException {
PdfDocument document = new PdfDocument();
document.parcelFileDescriptor = fd;
synchronized (lock) {
document.mNativeDocPtr = nativeOpenDocument(getNumFd(fd), password);
}
return document;
}
The fonction nativeOpenDocument from the lib doesn't seem to load.
I found some topic on github talking about it : https://github.com/barteksc/AndroidPdfViewer/issues/538
https://github.com/barteksc/PdfiumAndroid/issues/54
https://github.com/mshockwave/PdfiumAndroid/issues/13
But no solution found, as suggested i tried to change the dependency, tried to shut down my computer and my phone, tried to invalide cache and restart, tried on emulator but nothing work.
It would be very nice if someone could help me with that?

I just found out that in my case i had an app and a module where is my own library aar where i use the androidpdfviewer.
Probleme was that i had the dependency only on the lib and not the app.
To solve the probleme i had to add the dependency in both the app and the library. It's seem obvious now that i see that i have 2 build.gradle. Now it's working fine.
Add classpath 'com.github.barteksc:pdfium-android:1.9.0' in you build.gradle but the project not the module.

This is how I'm loading pdf url:
fragment_pdf_reader.xml:
<androidx.constraintlayout.widget.ConstraintLayout
//....
<com.github.barteksc.pdfviewer.PDFView
android:id="#+id/pdfView"
android:layout_width="match_parent"
android:layout_height="#dimen/zeroDp"
android:visibility="visible"
app:layout_constraintBottom_toTopOf="#+id/tvFeedback"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/view" />
//...
</androidx.constraintlayout.widget.ConstraintLayout>
Now inside your fragment get pdf url and make Http request with that pdf url and get ResponseBody pass it to the below method:
private fun downloadFile(responseBody: ResponseBody) {// get responseBody after making Http req . I'm using retrofit
binding?.let {
//it.pdfView.removeAllViews()
it.pdfView.fromBytes(responseBody.bytes())
.defaultPage(pageNumber)
.onPageChange(this#PdfReaderFragment)
.enableAnnotationRendering(true)
.onLoad(this#PdfReaderFragment)
.spacing(10) // in dp
.onPageError(this#PdfReaderFragment)
.onError { onError("File not in PDF format or corrupted") }
.load()
}
}
Implement these call backs :
OnPageChangeListener
OnLoadCompleteListener
OnPageErrorListener
implementation of above call backs are:
override fun loadComplete(nbPages: Int) {
binding?.let {
printBookmarksTree(it.pdfView.tableOfContents, "-")
}
}
override fun onPageError(page: Int, t: Throwable?) {
Log.d("PDF", t?.message)
}
override fun onPageChanged(page: Int, pageCount: Int) {
pageNumber = page
Log.d("PDF", "Page number $pageNumber $pageCount")
setTitleMessageBackArrow(String.format("%s %s / %s", "your fragment title", page + 1, pageCount))
}
private fun printBookmarksTree(tree: List<Bookmark>, sep: String) {
for (b in tree) {
Log.e("PDF", String.format("%s %s, p %d", sep, b.title, b.pageIdx))
if (b.hasChildren()) {
printBookmarksTree(b.children, "$sep-")
}
}
}

Related

Kotlin File.deleteRecursively() doesn't work on cacheDir

I'm trying to delete the app's cache subfolder (or even the cache folder itself), but nothing happens.
cacheFolder = File(this.cacheDir, "/download")
deleteFolder(cacheFolder)
private fun deleteFolder(fileOrDirectory : File){
fileOrDirectory.deleteRecursively()
}
The one-liner doesn't work too
this.cacheDir.deleteRecursively()
Any ideas?
It will be better if you use a service to delete the cacheFiles
This is how I did
class DeleteCache(context: Context, workerParams: WorkerParameters) :
Worker(context, workerParams) {
override fun doWork(): Result {
return try {
applicationContext.cacheDir?.let {
if (it.exists()) {
val entries = it.listFiles()
if (entries != null) {
for (entry in entries) {
entry.delete()
}
}
}
}
Result.success()
} catch (e: Exception) {
Result.failure()
}
}
}
The File.deleteRecursively() function unfortunately doesn't tell you the reason for failure to delete. It just returns true or false to show whether the deletion was successful. If it returns false, the directory tree may have been partly deleted.
Instead of using the old java.io.File class, which this function is a Kotlin extension of, it is preferable to use the "newer" java.nio utility classes because these will throw an IOException that describes the reason for failure to delete. Unfortunately, Kotlin does not currently provide a deleteRecursively() function that uses java.nio. It only provides a non-recursive deleteExisting() function on the Path class. So, you could instead write your own Path.deleteRecursively() extension function to live alongside Path.deleteExisting():
import java.io.IOException
import java.nio.file.FileVisitResult
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.SimpleFileVisitor
import java.nio.file.attribute.BasicFileAttributes
import kotlin.io.path.deleteExisting
fun Path.deleteRecursively() {
Files.walkFileTree(
this,
object : SimpleFileVisitor<Path>() {
override fun visitFile(file: Path, attrs: BasicFileAttributes?): FileVisitResult {
file.deleteExisting()
return FileVisitResult.CONTINUE
}
override fun postVisitDirectory(dir: Path, exc: IOException?): FileVisitResult {
if (exc != null)
throw exc
dir.deleteExisting()
return FileVisitResult.CONTINUE
}
}
)
}
You can then use it like this:
import kotlin.io.path.Path
...
Path(cacheDir).deleteRecursively()
and if it fails to delete the whole tree, you'll see a stack trace with an exception and a descriptive message.
Update:
The forthcoming Kotlin 1.8.0 will include an experimental function by the same name in its standard library.
Thanks for support. Now I got it working. The problem was the DownloadManager
val url = "$targetUrlFTP/$appName/$downloadingObject/$onlineVer.zip"
val request = DownloadManager.Request(Uri.parse(url))
request.setAllowedNetworkTypes((DownloadManager.Request.NETWORK_MOBILE or
DownloadManager.Request.NETWORK_WIFI))
request.setDestinationInExternalFilesDir(this, cacheFolder.toString(),
zipFileName)
val manager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
downloadID = manager.enqueue(request)
the line request.setDestinationInExternalFilesDir(this, cacheFolder.toString(), zipFileName)completely messed up the path.
Now I have
cacheFolder = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)!!
zipFile = File(cacheFolder, zipFileName)
...
request.setDestinationUri(Uri.fromFile(zipFile))
and everything works. Even
private fun deleteFolder(fileOrDirectory : File){
fileOrDirectory.deleteRecursively()
}
does.
Next step is to implement java.nio. Thank you Klitos for updating your post. I had no idea how to use it.

Retrofit image download works but the file is always corrupted

Here is my Retrofit API:
#GET
suspend fun downloadMedia(#Url url: String): Response<ResponseBody>
Here is the code that actually downloads the image from the URL and saves it to the device storage:
override fun downloadMedia(url: String): Flow<RedditResult<DownloadState>> = flow {
preferences.downloadDirFlow.collect {
if (it.isEmpty()) {
emit(RedditResult.Success(DownloadState.NoDefinedLocation))
} else {
// Actually download
val response = authRedditApi.downloadMedia(url)
if (response.isSuccessful) {
val treeUri = context.contentResolver.persistedUriPermissions.firstOrNull()?.uri
treeUri?.let { uri ->
val directory = DocumentFile.fromTreeUri(context, uri)
val file = directory?.createFile(
response.headers()["Content-Type"] ?: "image/jpeg",
UUID.randomUUID().toString().replace("-", ""))
file?.let {
context.contentResolver.openOutputStream(file.uri)?.use { output ->
response.body()?.byteStream()?.copyTo(output)
output.close()
emit(RedditResult.Success(DownloadState.Success))
}
} ?: run {
emit(RedditResult.Error(Exception("Unknown!")))
}
}
} else {
emit(RedditResult.Error(IOException(response.message())))
}
}
}
}
The file downloads and is the correct size in MB, but it somehow becomes corrupted with dimensions of 0x0 and just a blank image (when on my PC it can't even be opened).
I don't really know what I'm doing wrong as the file is being created and written to fine (which was difficult with SAF in and of itself).
Edit: I've also tried with and without #Streaming on the API function with the same results.
Turns out I was being an idiot. I was using a Retrofit instance which was using a MoshiConverter, this caused the content length to be changed and therefore the file was corrupted. Solved by using a Retrofit instance without a MoshiConverter.

Tensorflow-Lite unresolved reference for tfLite!!.setUseNNAPI in kotlin Android App

I am trying to run this app which is an object detection application. The app uses Tensorflow-Lite.
When trying to run this app I get the error
Unresolved reference: setUseNNAPI
for these lines
override fun setUseNNAPI(isChecked: Boolean) {
if (tfLite != null) tfLite!!.setUseNNAPI(isChecked)
}
It cannot find a reference for tfLite!!.setUseNNAPI(isChecked). This function should somehow be connected to the Interpreter options but these are set in the create method:
#Throws(IOException::class)
fun create(
assetManager: AssetManager,
modelFilename: String,
labelFilename: String,
inputSize: Int,
isQuantized: Boolean): Classifier {
...
try {
val options = Interpreter.Options()
options.setNumThreads(4)
options.setUseNNAPI(false)
d.tfLite = Interpreter(loadModelFile(assetManager, modelFilename), options)
} catch (e: Exception) {
throw RuntimeException(e)
}
Does anyone have a clue of what is going on?
Please check the build.gradle file for app.
If you see the below line or if the version is less than 2.3.0 update it to the latest version
implementation 'org.tensorflow:tensorflow-lite:0.0.0-nightly'
change it to
implementation 'org.tensorflow:tensorflow-lite:2.3.0'
it is not downloading the latest tensorflow-lite version for android.
Hope this fixes your issue

Symbol$CompletionFailure: class file for ActivityDeparturesBinding not found

I have a separate Android library in a separate project for an Android app. However I link the library directly (referencing the library module directly by path or the generated AAR) I get the following error:
A problem was found with the configuration of task ':app:kaptDevelopDebugKotlin'.
> Cannot write to file '/home/m0skit0/Repos/repo/app-android/app/build/intermediates/data-binding/develop/debug/bundle-bin' specified for property 'dataBindingArtifactOutputDir' as it is a directory.
e: error: cannot access ActivityDeparturesBinding
class file for com.blablabla.databinding.ActivityDeparturesBinding not found
Consult the following stack trace for details.
com.sun.tools.javac.code.Symbol$CompletionFailure: class file for com.blablabla.databinding.ActivityDeparturesBinding not found
* What went wrong:
Execution failed for task ':app:kaptDevelopDebugKotlin'.
This shows just after the task :app:transformDataBindingWithDataBindingMergeArtifactsForDevelopDebug
However the class does exist, with full canonical name. It belongs to the the library and it is correctly auto generated (without any errors) by Android's Data Binding processor on both projects. The library compiles correctly on its own. There's no further stacktrace even if the compilation is run with --stacktrace.
I have tried linking the library with compile, implementation and api, all with the same result.
Gradle version is 4.4.
UPDATE:
Updating to Gradle 5 did not solve the problem. Renaming the XML did not solve the problem.
I've also found that the error happens only when I reference the ActivityDeparturesBinding class in my code, even if the part where it is referenced is never actually called. Only importing it without referencing it does not cause the error.
UPDATE2:
This is the activity that uses the layout:
class DeparturesActivity : BaseVinPlateActivity<DeparturesViewModel, ActivityDeparturesBinding>() {
companion object {
fun getStartIntent(context: Context) = Intent(context, DeparturesActivity::class.java)
}
#Inject
override lateinit var viewModel: DeparturesViewModel
override val layout: Int = R.layout.activity_departures
override fun injectThis(component: ActivityComponent) {
component.inject(this)
}
override fun getToolbarTitleId(): Int = R.string.departures_title
override fun initializeDataBindings(dataBinding: ActivityDeparturesBinding) {
dataBinding.viewmodel = viewModel
}
}
abstract class BaseVinPlateActivity<T: BaseVinPlateViewModel, U: ViewDataBinding> : CompoundsBaseActivity() {
protected abstract var viewModel: T
protected abstract val layout: Int
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == BARCODE_RESULT_CODE_ACTIVITY && resultCode == Activity.RESULT_OK) {
val barcodeResult = data?.getStringExtra(BARCODE_RESULT_ACTIVITY)
viewModel.setBarcodeResult(barcodeResult)
}
}
override fun initializeView() {
super.initializeView()
val dataBinding = inflateDataBinding<U>(layout)
initializeDataBindings(dataBinding)
with (viewModel) {
setBindValidator(Validator(dataBinding))
loadData()
}
}
protected abstract fun initializeDataBindings(dataBinding: U)
}
If I remove the initializeDataBindings() function, the error is gone. Note that commenting only the body of the function is not enough, I had to remove the whole function.
This smells like a compiler/tools bug.
I know it's late but I'd like to say that something similar happened to me, in my case I have a library module which was using a third party library, and then in my app module I'm using dagger for injecting dependencies, and I was getting this issue because, I believe dagger couldn't find this thrid party library's class because it wasn't directly implemented in the app's dependencies. So in my case what got it sorted was to change the way I was adding the third party library into my local library module in order to use api instead of implementation.
We could bypass the compilation error in a couple of ways (there are probably more ways). The best way we came up with is converting the abstract function into an abstract lambda property.
Parent class:
abstract class BaseVinPlateActivity<T: BaseVinPlateViewModel, U: ViewDataBinding> : CompoundsBaseActivity() {
protected abstract val initializeDataBinding: U.() -> Unit
// Rest of code
}
Child class:
class DeparturesActivity : BaseVinPlateActivity<DeparturesViewModel, ActivityDeparturesBinding>() {
override val initializeDataBinding: ActivityDeparturesBinding.() -> Unit = { viewmodel = this#DeparturesActivity.viewModel }
// Rest of code
}

How to display a 360 panorama from android application

I want to display a panorama from my android application, this panorama is online and I have its url I load it on a webview but it won't work properly. It just appears a portion of it, and it won't turn or move upside down. I have no idea where to start with this, can you point me at the right direction? thank you in advance
After a lot of research I found out this library it is still pretty new but sure can help you start with something. I'm posting it just to save time for other searchers! cheers
PanoramaGl
I had a similar situation in Kotlin, but I needed to get the image from a URL. I resolved it using Picasso. I leave this here for future reference:
val options = VrPanoramaView.Options()
val url = "https://urltoyourimage.com/image.jpg"
Picasso.get().load(url)
.into(object : Target {
override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) {
options.inputType = VrPanoramaView.Options.TYPE_MONO
vrPanoramaView.loadImageFromBitmap(bitmap, options)
}
override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) {
print(e?.localizedMessage)
}
override fun onPrepareLoad(placeHolderDrawable: Drawable?) {
print(placeHolderDrawable)
}
})
Add dependency in gradle (app-level)
compile 'com.google.vr:sdk-panowidget:1.80.0'
Add VrPanoramaView in your xml and get a reference via findViewById() method in android
Below is the code for loading the image from asset.
VrPanoramaView takes input as Bitmap so we need to first
convert it into right format. Here, the loadPhotoSphere functions
in called in onCreate()
private void loadPhotoSphere() {
VrPanoramaView.Options options = new VrPanoramaView.Options();
InputStream inputStream = null;
try {
inputStream = assetManager.open("image.jpg");
options.inputType = VrPanoramaView.Options.TYPE_MONO;
mVRPanoramaView.loadImageFromBitmap(BitmapFactory.decodeStream(inputStream), options);
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
Read about Google VR SDK for Android here

Categories

Resources