Show HN: Gander, an Android file viewer that asks for no permissions

github.com

182 points by mokshablr 15 hours ago

Hi HN,

I built an Android file viewer that opens PDF, Word, Excel, PowerPoint, images, video, audio, Markdown and code, and asks for no permissions at all.

I have always been uneasy about opening files people send me. On Android you either install a 400 MB office suite and sign in or use a small free viewer that wants storage access and ends up uploading your file to a server to render it. Also the hassle of having to download different apps for different file formats was really annoying.

Gander holds no permissions, not even INTERNET so the OS itself guarantees the file cannot leave the phone.

PDFs use Pdfium, media uses Media3, and Office formats are rendered by bundled JS libraries in a WebView and so no request goes to any server.

It is a viewer only. Complex PowerPoint decks come out approximately right, spreadsheet charts are not drawn, and old binary .doc and .ppt are unsupported. I'll work on it as issues come up :P

It is 14 MB, MIT licensed and uploaded on Github releases.

Do try it! I would love some feedback especially on files that render badly or need new support.

TekMol 13 hours ago

This is interesting.

I thought granting internet access to apps is not avoidable on Android.

When an app does not request internet, is it really guaranteed that it cannot talk to the outside world? Or is it having other avenues like opening a browser or some other component with a custom url or something?

Update: I just asked Gemini, and it does not look good:

An Android app without the INTERNET permission is not guaranteed to be isolated from the outside world. While it cannot make direct network connections itself, it can use several other mechanisms to transmit data externally:

Intent-Based Communication (The Browser) An app can launch an explicit or implicit Intent to hand data over to another app that does have internet access.

That means the app can open a system browser using a URL containing the data it wants to transmit.

It can also load a Chrome Custom Tab inside its own UI task, passing data through the URL string.

There is also Inter-Process Communication: If two apps from the same developer share a User ID (sharedUserId), they run in the same process and share all permissions, including internet access.

There is also the concept of Content Providers: Content Providers allow apps to share data. An offline app can write data into a shared database or a public Content Provider. A secondary, online-enabled app can then read that database and upload the contents.

  • sheept 13 hours ago

    I guess that makes sense. Since most non-privacy-focused Android distributions don't let users turn off the internet permission, keeping the permission secure likely ceased to be a priority.

    The full list of bypasses is likely much larger because it doesn't fall in the scope of bug bounties.

    • well_ackshually 8 hours ago

      permission.INTERNET has _never_ been a dangerous permission on android.

  • marak830 12 hours ago

    Christ that's depressing.

    I'm assuming that all works even with application sandboxing (1) or am I mis-reading how that is applied to applications.

    Man I need to move my movement to GrapheneOS up to be sooner.

    (1) https://source.android.com/docs/security/app-sandbox

    • csande17 11 hours ago

      Yeah, Android's sandbox deals with low-level file access and socket APIs and stuff. But Android also, intentionally, allows apps to expose their data and functionality to other apps via the higher-level "intent" system.

      Apps get to choose what permissions are needed to access their intents, so under Android's security model, really it's Chrome's fault (or whatever browser the user has installed) for exposing an intent that allows apps that don't have the INTERNET permission to exfoltrate data.

      Similarly, apps are also allowed to collude to share data with each other if they want; that's how stuff like Google Play Services works.

      • murderfs 10 hours ago

        > Apps get to choose what permissions are needed to access their intents, so under Android's security model, really it's Chrome's fault (or whatever browser the user has installed) for exposing an intent that allows apps that don't have the INTERNET permission to exfoltrate data.

        You might as well blame the phone for not being encased in concrete and thrown into a well. You might not want your text message application to have the internet permission, but you'd certainly want to be able to open links from it.

    • TekMol 10 hours ago

      As I understand it, this "Application Sandbox" is to prevent app A seeing what app B is doing.

      The problem here is a different one: The user granting an app access to their files and then having no way to prevent that app from sending that data to the developer of the app.

  • nottorp 12 hours ago

    You're aware that the only really secure computing device has no way to connect to a network and is under armed guard?

    Edit: also that LLMs are tuned for "engagement" not for answers like "it's secure enough, move along"?

  • andyjohnson0 10 hours ago

    This is all very true, unfortunately. Google designed Android as a an OS for internet appliances. They're not interested in local-only apps working on local files.

    But I think the OP is to be complimented for what they've built and their intention in making it local-only. I've long felt that apps that *genuinely" respect privacy are one of the few areas of opportunity still left in the mobile space. I wish there were more develpers building things like this.

  • chrisjj 10 hours ago

    > If two apps from the same developer share a User ID (sharedUserId), they run in the same process and share all permissions

    Good grief. What a huge vulnerability. Is there some benefit that justifies this?

    • j16sdiz 10 hours ago

      First, `sharedUserId` is opt-in.

      You need something like that for plugin / extension like system.

      Third, it is being deprecated. and people are screaming "bad google" "android is no longer free!"

    • well_ackshually 8 hours ago

      Literally there for more protection. Split your app into different apps, each handling a component. Want a banking app, without access to registering your card for NFC payments ? Split the app.

      sharedUserIds cannot be applied blindly, they need the app to be signed with the same key, both need to declare their sharedUserId to be the same, etc.

  • inigyou 10 hours ago

    GrapheneOS asks for internet permission when you install any app.

    • chrisjj 10 hours ago

      Even an app that does not want internet?

      • drunner 9 hours ago

        I don't see that as an option, but I'm not a GrapheneOS power user.

      • drupe 9 hours ago

        No, it only asks if the app requests the internet permission.

    • zombot 10 hours ago

      Can you deny it and is the situation the same as on Android?

      • drunner 9 hours ago

        You can on graphene. It's not uncommon to use Google keyboard this way for example.

  • yonatan8070 10 hours ago

    > That means the app can open a system browser using a URL containing the data it wants to transmit.

    This can be mitigated by using an app like URLCheck [1], which you set as your default browser, and it shows and lets you edit the URL being opened before handing it off to your real browser. It also has neat features like choosing to open in incognito (if you use Firefox) and automatically removing tracking parameters.

    [1] https://f-droid.org/packages/com.trianguloy.urlchecker

  • mokshablr 9 hours ago

    Yeah fair I was too flat in the post... No INTERNET means my process can't reach the network. It doesn't stop an app passing data to something that can, so your list is right.

    It's not a guarantee then, it's just a lot less to check. There's no silent route out so all that's left is what the app chooses to send via intents and Gander sends two, both when you tap something ("Share" and "Show in file manager")

    Nothing anywhere builds a URL out of the file contents. No browser launch, no Custom Tabs and no shared user ID. The only content provider is closed to other apps. The one time a file goes anywhere is when you hit Share and pick the app, and what it gets is read access to that one file.

    In case you grep it there's an https:// in ViewerActivity.kt. That's the local virtual host WebViewAssetLoader serves the bundled renderers from, it never leaves the process.

  • well_ackshually 8 hours ago

    "I just asked Gemini, and it turns out that Mossad can steal my data anyways".

    - An app without the INTERNET permission will crash the moment you try to access the internet. It's like a rite of passage of every android developer with every new project, you forgot the permission.

    - Launching an Intent is EXTREMELY visible. It opens a full on browser. It's limited to a GET with a dedicated URL, so what are they going to do, stuff your data in query params ?

    - Even in the case of another app being installed that would silently receive this intent and not pop an activity, you need a different app. It cannot be an activity added by the same package.

    - Loading a Chrome Custom Tab opens a whole ass browser in front of you, think you're going to miss it ?

    - Shared user IDs also require two different app installations and you cannot declare multiple. You also cannot have sharedUserId if you do not sign the apps with the same key. you cannot sharedUserId with Facebook, you need once again a dedicated app installation.

    - A ContentProvider needs a dedicated permission on the writing app AND on the reading app, which is once again very visible.

    I'll add more to your list: an app can request to write a file inside your downloads folder, and another one can show a popup asking you to open it! And if someone shows up with a hammer at your door, he can also hit your fingers really hard to make you tell him the data.

    Android apps are, by default, very well isolated. INTERNET is a permission like any other, just not surfaced as a dangerous one (like permanent access to your background location would be), or a runtime one (like access to your current location while the app is in foreground)

  • exe34 5 hours ago

    I use a rooted phone and afwall+ which uses the underlying Linux firewall. There's also the mobile and WiFi data permissions you can change manually.

  • reorder9695 2 hours ago

    AOSP allows the internet permission to be denied per app (hence LineageOS and Graphene exposing it), it's just most OEMs ROMs don't expose it, you can draw your own conclusions from that.

aand16 8 hours ago

Please add signature to readme so users can verify before installing with Obtainium eg:

5B:5C:F6:4A:94:23:7C:D5:F0:E0:85:76:00:38:BC:1C:EB:DF:18:DA:BA:5C:B3:EA:CA:7C:15:9F:22:A7:E2:4B

  • mokshablr 2 hours ago

    Added, it's in the README under Install now. Thanks for bringing that up!

dheerajvs 13 hours ago

Tried it and I love it. Good work! Have you considered hosting it on F-Droid?

  • clort 12 hours ago

    That is listed in the Roadmap section right at the end of the README.md file

  • netfortius 10 hours ago

    Obtainium works just fine with the github source

adamddev1 12 hours ago

This looks great. It seems crazy how we got to this point where this is a revolutionary, unique product.

atmanactive 7 hours ago

Thanks for sharing. Could it be possible to add optional force-dark mode for documents as well? The feature list mentioned dark mode, but that's for app's own UI. The really awesome feature would be to be able to invert the document colors so that the document itself is shown in dark mode. Thanks.

sgc 13 hours ago

Can you add odt, ods, etc support? I use libreoffice viewer but it would be nice to reduce the number of apps on my device.

  • mokshablr 8 hours ago

    ods actually already works.. it goes through the same SheetJS path as xlsx and csv. But your question made me check properly and there's a bug... I never registered the OpenDocument mime type so Gander wont show up in the "open with" list when you tap a .ods. Opening it from inside the app works fine.

    Fixed in the manifest just now, it'll be in the next build.

    odt and odp dont work at all. odt is a fair ask.. its the same shape of problem as docx so it's doable. Less sure about odp though.. the pptx renderer is already the weakest part of this.

    And yeah, fewer apps on the phone was pretty much the whole reason I started building this.

nextaccountic 10 hours ago

Could you publish to a store? Either/or Google Play or F-Droid

getpokedagain 5 hours ago

Going to test this out. I've been very happy to see the number of new apps being developed to be installed on android via obtanium and away from the Google app store. I assume it's llms enabling folks to get quick apps out.

Mobeen0119 15 hours ago

Since everything is offline, what ended up being the hardest format to support while keeping rendering faithful?

  • vrighter 13 hours ago

    Why would rendering a document need internet access?

    • Chilinot 13 hours ago

      PDFs can do all sorts of shenanigans, such as bundle executable javascript that runs when the file is opened.

      • echoangle 12 hours ago

        You don’t need internet to run JS though. And the JS seems to be pretty sandboxed in normal viewers too, so a pdf file shouldn’t make network requests on its own. As long as there is no iframe-like content I don’t see how rendering a document would ever need network.

    • chrisjj 10 hours ago

      Apart from exfiltration, no reason at all.

  • mokshablr 9 hours ago

    pptx by a mile. A slide is absolutely positioned shapes with a whole layout inheritance thing going on so the JS renderer ends up reimplementing a whole layout engine, and it gets you to "approximately right" and stops there. Charts I gave up on.

    docx was annoying in a different way. Bullets came out as junk characters for a while and I couldnt work out why. I then found that Word writes them as private use area codepoints in the Symbol font so unless you have that exact font you get some junk. I just map them back to unicode after the render.

    xlsx was the easy one, SheetJS does the hard part.

    On faithful though none of this is faithful the way LibreOffice is. Its more that "you can read the document and it looks roughly right" which is what I wanted out of an app of 14MB.

array4277 7 hours ago

The app is pleasingly very responsive. I don't think I've used anything else on Android that lets me open and close PDFs rapidly without getting bogged down. Nice!

collabs 9 hours ago

Two things I really like:

1. Markdown loads as preview

2. Plain text files wrap instead of scrolling to the right

Good job and thank you for sharing

celsoazevedo 4 hours ago

Can this open .rtf files?

I know it's old, but I still receive them from time to time.

tim-projects 12 hours ago

Sounds great! You'll just need your social security or passport id sent to google to install and use it... sobs

est 12 hours ago

This is super cool.

> Requirements: JDK 17+ and the Android SDK (platform 35).

Bummer. I am stuck with Android 14 for the moment.

  • notpushkin 11 hours ago

    Which is also weird considering most of formats are just opened in a WebView with a respective JS-based viewer. Surely it can be backported?

  • shscs911 11 hours ago

    Just installed it in my Android 10 phone and it works.

  • mokshablr 9 hours ago

    Sorry, that's on my README. That line is under "Build from source" so it's what you need to compile it, not to run it. minSdk is 26, so Android 8 and up.

    Android 14 is well past the floor, you're good to go!

mathfailure 9 hours ago

I don't understand the comments to this post. Don't people use firewalls anymore to limit internet access only to the apps really needing it?

  • PennRobotics 8 hours ago

    I had the goal to eliminate annoying ads in some apps that I use. I deleted the user data for those apps and also applied restrictions so they could no longer access internet. Ads still showed up.

    I mostly stopped using these types of apps and switched to F-Droid alternatives.

  • atmanactive 7 hours ago

    Could you please recommend a comfortable personal firewall app for Android?

    • ies7 5 hours ago

      Rethink: dns + firewall + vpn

  • fg137 7 hours ago

    Firewalls are already a pain for desktop OS. They are basically unusable on mobile for normal users.

freedomben 9 hours ago

Great work! A tool like this is sorely needed.

How is accessibility? Do PDFs work with screenreaders for example?

noja 10 hours ago

This is a great idea - but doesn't Android inject network permission into all apks? It did in the past.

carloslfu 7 hours ago

this is cool! It's amazing that it's still such a pain to do something like this with built-in Android app.

chrisjj 10 hours ago

Well done!

> A tiny, open source, fully offline file viewer for Android that opens PDF, Word,

Perhaps correct Word to DOCX. Word is not just DOCX and DOCX is not just Word. This user needs DOC viewing and would be disappointed having installed this app.

I wish you every success with this.

chesscoachx 10 hours ago

Great. You built an excellent tool to solve a real-world problem: being able to open files on the fly without granting internet access and without having to install 400 MB bloated suites that track everything.

  • weberer 6 hours ago

    Thanks, Claude