Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android port of koreader #320

Closed
chrox opened this issue Oct 17, 2013 · 42 comments
Closed

Android port of koreader #320

chrox opened this issue Oct 17, 2013 · 42 comments

Comments

@chrox
Copy link
Member

chrox commented Oct 17, 2013

Given that all thirdparty libraries are available in Android, I think it's time to consider it. And I believe koreader is able to knock out a ton of readers in that platform.

@houqp
Copy link
Member

houqp commented Oct 17, 2013

Ha, I am pretty sure it's going to become the best reader :) But I am not likely to have time working on this until December :(

@hwhw is also the author of droidreader, he should have a lot of experience on android development.

@giorgio130
Copy link
Member

Well, that would attract a lot of developers I think! And new features and fixes for everybody. Nice idea.

@houqp
Copy link
Member

houqp commented Oct 17, 2013

The only thing that bothers me is how to handle our relationship with coolreader (if the android port really happened). It makes me feel bad to just grab coolreader's engine and make another reader out of it in it's main target market :(

Is it possible to port our pdf/djvu stack to coolreader?

@hwhw
Copy link
Member

hwhw commented Oct 17, 2013

A straight port shouldn't be too hard. That would mean keeping our own UI. I'm not sure if that would be desirable... But then, I'm used to it :-P Adding other blitbuffer layouts (color!) would be a logical next step there.

It would be a Java shim that creates a pane to blit to, passing events to native code, and let the rest be handled by native code. Since Android 2.something one can directly blit to on-screen surfaces.

I agree with houghp that porting our page-based document stack to CR would be an interesting idea, too.

@houqp
Copy link
Member

houqp commented Oct 17, 2013

Actually I remember seeing a djvu branch in coolreader's repo ;p It's author might actually considered adding page-based document stack before.

@hwhw
Copy link
Member

hwhw commented Oct 24, 2013

We probably could easily use this to make a first try: http://developer.android.com/reference/android/app/NativeActivity.html - we would not have native UI elements, but rather the "original" feel of Koreader. I will concentrate on two other things first: FFI-bindings for input and output, and a multi-bpp blitbuffer implementation. In theory, we could go "colored" in Android then.

@chrox
Copy link
Member Author

chrox commented Oct 24, 2013

I like the touch-to-go UI of koreader without any fading or animation. So the straight feel is OK for me. :)

@chrox
Copy link
Member Author

chrox commented Oct 26, 2013

I think the easiest port for Android is creating a launcher in the Android part and use libsdl to handle the input and output like in the emulator. Other parts of koreader are highly portable that should run on virtually any modern handheld computing devices.

@hwhw
Copy link
Member

hwhw commented Jan 1, 2014

OK, I now had a deep look into this and have made good progress already. I have kind of a "luajit-trampoline", which - as a NativeActivity - loads the LuaJIT interpreter. I verified that LuaJIT can then run as a main thread, doing native Android API calls via FFI (!). This works just great. I already interfaced with the blitbuffer implementation and am able to read input events. I think I'll need about 1-2 days and I'll have some first version ready. It will still be a bit rough around the edges, but it's going great for now.

I dumped the idea to use SDL for now. SDL2 has a nice Android implementation, but it comes with a big gotcha: It has a polling event loop, keeping the CPU busy and needlessly eating the battery. For a game, that might be negligible, but for a reader not so much. That said, the Android native activity API is nice enough to just go with it.

@houqp
Copy link
Member

houqp commented Jan 2, 2014

Exciting news!!

Cannt wait to see your first demo :)

@chrox
Copy link
Member Author

chrox commented Jan 5, 2014

That's great! I cannot wait to try it.

@hwhw
Copy link
Member

hwhw commented Jan 7, 2014

It turns out to be a bit harder than I initially expected. This is not the fault of the approach chosen: That still looks promising. If someone would like to have a try: https://github.com/hwhw/android-luajit-launcher

The hard part, however, is to persuade the software stack we are using to work with the Android toolchain, which lacks a few things and cooperates badly with old versions of libtool, config.sub/config.guess and other stuff. It's a bit awful to work through compile and link failures over and over...

So it may still take a few days.

@ghost ghost assigned giorgio130 Jan 7, 2014
@chrox
Copy link
Member Author

chrox commented Apr 9, 2014

The launcher is really cool. But I failed to load library with ffi.load("libmupdf.so") in main.lua after adding libmupdf.so in the lib directory of the package. And a google search shows that for security reason the dynamic library loading mechanism underneath the android ndk exposes only paths in LD_LIBRARY_PATH which contains only /system/lib for library search. And it's not possible to modify LD_LIBRARY_PATH on the fly from the native code, for us from the luajit side. How could this be solved?

@chrox chrox changed the title Is someone considering an Android port of koreader? Android port of koreader Apr 12, 2014
@hwhw
Copy link
Member

hwhw commented Apr 14, 2014

Hm, I'm just back from a few weeks of vacation, now I need to keep up with a few things and then I really hope I can get back to this. We should be allowed to load libraries from the applications data folder, so we will need to clone the launcher and make it the base of a (specific) Android application, I think. But I need to get back into the remains of my project folders, I think I played a bit with such things already.

@houqp
Copy link
Member

houqp commented May 13, 2014

I can load libmupdf with absolute path, but can't force dlopen to search dependencies in app data directory :(

@houqp
Copy link
Member

houqp commented May 13, 2014

@houqp
Copy link
Member

houqp commented May 13, 2014

Another thing. Since android port will have a completely different source tree structure, we should maybe start factor out the frontend part from koreader into a separate reusable git repo.

@chrox
Copy link
Member Author

chrox commented May 15, 2014

I was thinking to compile all base libraries into one big static lib to circumvent library dependency hell on Android.

@hwhw
Copy link
Member

hwhw commented May 15, 2014

Yes, I guess this is possible. But licensing issues might prevent this...

On 15. Mai 2014 14:10:20 MESZ, Huang Xin notifications@github.com wrote:

I was thinking to compile all base libraries into one big static lib to
circumvent library dependency hell on Android.


Reply to this email directly or view it on GitHub:
#320 (comment)

Diese Nachricht wurde von meinem Android-Mobiltelefon mit K-9 Mail gesendet.

@houqp
Copy link
Member

houqp commented May 15, 2014

@hwhw , can you explain more about the licensing issue?

@chrox
Copy link
Member Author

chrox commented May 18, 2014

How about loading all libraries in the android_main like here https://github.com/MIvanchev/hx-gameplay/blob/master/haxelib/native-glue/android/src/android-main.cpp#L145.

It first resolves the application data directory by fiddling with the jvm and then loads necessary libraries with the absolute path. And it seems to work without big re-factoring of ffi code.

local mupdf = ffi.load("mupdf")
A.LOGI("mupdf library loaded "..tostring(mupdf))

The log shows:

I/luajit-launcher( 5587): mupdf library loaded userdata: 0x4ef001e0

@chrox
Copy link
Member Author

chrox commented May 18, 2014

And I also found a great script to compile GNU world programs in Android. http://forum.xda-developers.com/showthread.php?t=1444792.

@houqp
Copy link
Member

houqp commented May 19, 2014

yeah, we can construct the absolute path that way.

i don't understand how the ffi.load in your example works tough. did you intercept the call then fix the path and dependencies transparently?

@chrox
Copy link
Member Author

chrox commented May 19, 2014

Since ffi.load also uses dlopen to load dynamic library. It should be able to resolve "mupdf" as loaded and returns the library handler directly. See http://man7.org/linux/man-pages/man3/dlopen.3.html

@houqp
Copy link
Member

houqp commented May 19, 2014

Right, but I have to use absolute path, otherwise I will get the error: dlopen failed: library "libmupdf.so" not found. Also I have to manually resolve the dependencies.

@chrox
Copy link
Member Author

chrox commented May 19, 2014

Yes. But if you loaded the "libmupdf.so" before in android_main then ffi.load in the lua side would load "mupdf" without problem.

And I also have a good news. I finally succeeded to compile almost all libraries for Android except sdcv and djvulibre, though I don't know if these libraries will work on a real Android device.

@houqp
Copy link
Member

houqp commented May 19, 2014

Oh, I see. Should have read your post more carefully ;p

Are you using android emulator right now?

@chrox
Copy link
Member Author

chrox commented May 19, 2014

No. I just compiled the base libraries with a standalone toolchain from NDK and made some minor changes of the makefile. Still don't know if they would run at all.

@chrox
Copy link
Member Author

chrox commented May 19, 2014

But it could be a start I think. I will send a PR and create an android branch tomorrow.

@hwhw
Copy link
Member

hwhw commented May 19, 2014

Hi, pretty cool! I was marching the same path, but didn't get that far.

@chrox
Copy link
Member Author

chrox commented May 21, 2014

The lo_dlopen way seems working. We can still use the current ffi.load to load arbitrary library in libs in this way

local mupdf = ffi.load("libs/libmupdf.so")
A.LOGI("mupdf library loaded "..tostring(mupdf))

After wrap the ffi.load in android.lua this way:

ffi.load = function(name, ...)
        return ffi.C.lo_dlopen(ffi.cast("char*", name))
end

The result goes:

I/luajit-launcher(10979): dlopen(/system/lib/libdl.so) = 0x400c40ac
I/luajit-launcher(10979): dlopen(/system/lib/libc.so) = 0x400c4cf8
I/luajit-launcher(10979): dlopen(/data/data/org.koreader.launcher/libs/libjpeg.so.9) = 0x400c9964
I/luajit-launcher(10979): dlopen(/data/data/org.koreader.launcher/libs/libjpeg.so.9) = 0x400c9964
I/luajit-launcher(10979): dlopen(/data/data/org.koreader.launcher/libs/libfreetype.so.6) = 0x400c9a88
I/luajit-launcher(10979): dlopen(/system/lib/libm.so) = 0x400c4f40
I/luajit-launcher(10979): dlopen(/system/lib/libstdc++.so) = 0x400c4e1c
I/luajit-launcher(10979): dlopen(/system/lib/liblog.so) = 0x400c4bd4
I/luajit-launcher(10979): dlopen(/data/data/org.koreader.launcher/libs/libmupdf.so) = 0x400c9bac
I/luajit-launcher(10979): mupdf library loaded cdata<void *>: 0x400c9bac

@hwhw
Copy link
Member

hwhw commented May 21, 2014

Wow, that is pretty cool!

@houqp
Copy link
Member

houqp commented May 21, 2014

\o/ So what is left is mapping the input and output between koreader and android?

@hwhw
Copy link
Member

hwhw commented May 22, 2014

Yes, I think so. And that shouldn't be too hard either. The BlitBuffer abstraction is capable of using what Androids provides us with - that would be for output. However, for input, we need to think on what level we want to start. We could synthesize Linux Input style events, just as the emulator does. It's probably the easiest way to start.

@chrox
Copy link
Member Author

chrox commented May 22, 2014

OK. Both Lua C module and native library loading are tested on Android.

local mupdf = ffi.load("libs/libmupdf.so")
A.LOGI("mupdf library loaded "..tostring(mupdf))
local leptonica = ffi.load("libs/liblept.so.3")
A.LOGI("leptonica library loaded "..tostring(leptonica))
local tesseract = ffi.load("libs/libtesseract.so.3")
A.LOGI("tesseract library loaded "..tostring(tesseract))
local k2pdfopt = ffi.load("libs/libk2pdfopt.so.2")
A.LOGI("k2pdfopt library loaded "..tostring(k2pdfopt))
local crengine = ffi.load("libs/libcrengine.so")
A.LOGI("crengine library loaded "..tostring(crengine))

local pdf_loaded = require("libs/libkoreader-pdf")
A.LOGI("koreader-pdf loaded "..tostring(pdf_loaded))

local cre_loaded = require("libs/libkoreader-cre")
A.LOGI("koreader-cre loaded "..tostring(cre_loaded))
I/luajit-launcher(16412): dlopen(/system/lib/libdl.so) = 0x400c40ac
I/luajit-launcher(16412): dlopen(/system/lib/libc.so) = 0x400c4cf8
I/luajit-launcher(16412): dlopen(/data/data/org.koreader.launcher/libs/libjpeg.so.9) = 0x400c9964
I/luajit-launcher(16412): dlopen(/data/data/org.koreader.launcher/libs/libfreetype.so.6) = 0x400c9a88
I/luajit-launcher(16412): dlopen(/system/lib/libm.so) = 0x400c4f40
I/luajit-launcher(16412): dlopen(/system/lib/libstdc++.so) = 0x400c4e1c
I/luajit-launcher(16412): dlopen(/system/lib/liblog.so) = 0x400c4bd4
I/luajit-launcher(16412): dlopen(/data/data/org.koreader.launcher/libs/libmupdf.so) = 0x400c9bac
I/luajit-launcher(16412): mupdf library loaded cdata<void *>: 0x400c9bac
I/luajit-launcher(16412): dlopen(/data/data/org.koreader.launcher/libs/libz.so.1) = 0x400c9cd0
I/luajit-launcher(16412): dlopen(/data/data/org.koreader.launcher/libs/libpng16.so.16) = 0x400c9df4
I/luajit-launcher(16412): dlopen(/data/data/org.koreader.launcher/libs/liblept.so.3) = 0x400c9f18
I/luajit-launcher(16412): leptonica library loaded cdata<void *>: 0x400c9f18
I/luajit-launcher(16412): dlopen(/data/data/org.koreader.launcher/libs/liblept.so.3) = 0x400c9f18
I/luajit-launcher(16412): dlopen(/data/data/org.koreader.launcher/libs/libtesseract.so.3) = 0x400ca03c
I/luajit-launcher(16412): tesseract library loaded cdata<void *>: 0x400ca03c
I/luajit-launcher(16412): dlopen(/data/data/org.koreader.launcher/libs/libmupdf.so) = 0x400c9bac
I/luajit-launcher(16412): dlopen(/data/data/org.koreader.launcher/libs/libtesseract.so.3) = 0x400ca03c
I/luajit-launcher(16412): dlopen(/data/data/org.koreader.launcher/libs/libk2pdfopt.so.2) = 0x400ca160
I/luajit-launcher(16412): k2pdfopt library loaded cdata<void *>: 0x400ca160
I/luajit-launcher(16412): dlopen(/data/data/org.koreader.launcher/libs/libcrengine.so) = 0x400ca284
I/luajit-launcher(16412): crengine library loaded cdata<void *>: 0x400ca284
I/luajit-launcher(16412): trying to open asset libs/libkoreader-pdf.lua: cdata<struct AAsset *>: NULL
I/luajit-launcher(16412): dlopen(/data/data/org.koreader.launcher/libs/libk2pdfopt.so.2) = 0x400ca160
I/luajit-launcher(16412): dlopen(/data/data/org.koreader.launcher/libs/libluajit.so) = 0x400ca3a8
I/luajit-launcher(16412): dlopen(/data/data/org.koreader.launcher/libs/libkoreader-pdf.so) = 0x400ca4cc
I/luajit-launcher(16412): koreader-pdf loaded true
I/luajit-launcher(16412): trying to open asset libs/libkoreader-cre.lua: cdata<struct AAsset *>: NULL
I/luajit-launcher(16412): dlopen(/data/data/org.koreader.launcher/libs/libcrengine.so) = 0x400ca284
I/luajit-launcher(16412): dlopen(/data/data/org.koreader.launcher/libs/libkoreader-cre.so) = 0x400ca5f0
I/luajit-launcher(16412): koreader-cre loaded true

@chrox
Copy link
Member Author

chrox commented May 22, 2014

Another thing we need to do is packing all libraries into one zip file (or 7z) in asset and unpack the libraries into application data directory at run time.

@hwhw
Copy link
Member

hwhw commented May 28, 2014

We're there! Well, a bit of work left, but it works nicely!

We need to flesh out the things left and then we need to enter the stores - Google Play, probably, and maybe f-droid? Maybe we should ask for reviews on xda-developers, so we have initial feedback for a bunch of devices? That way we could avoid collecting bad feedback on Google Play with end users stumbling over this and that...

Or maybe mobileread instead? There are quite a lot of people with Android devices there now and the discussion atmosphere is very friendly.

Probably we need to start heavy debugging on our own first. And then maybe start nightly builds for Android.

I'm very pleased regarding the low memory footprint. However, due to the animation durations, it feels a bit sluggish. We probably should make these shorter for Android devices.

@hwhw
Copy link
Member

hwhw commented May 28, 2014

Oh - and we should probably allow color rendering...

@chrox
Copy link
Member Author

chrox commented May 28, 2014

Yes. We almost get there.

@Markismus
Copy link
Member

Mobileread seems like a good place for testing.

XDA-developers seems like a good place for getting more developers.

Google play would be nice for the next stable release. I feel we should get a bit more momentum before we enter the fray.

All nice and dandy, but can't wait for the time to install it on my old HTC phone. :)

@houqp
Copy link
Member

houqp commented May 28, 2014

Yeah, definitely release in hacker communities first before going to google play. But first of first, we need to write some development document!

@chrox
Copy link
Member Author

chrox commented Jun 1, 2014

Close this now since the first android build is out.

@chrox chrox closed this as completed Jun 1, 2014
houqp pushed a commit to houqp/koreader that referenced this issue Apr 24, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants