Open-source project
bingoogolapple/BGAPhotoPicker-Android avatar
bingoogolapple/BGAPhotoPicker-Android

BGAPhotoPicker-Android: file paths, a vendored PhotoView, and no tag since 2016

Android 图片选择、预览、九宫格图片控件、拖拽排序九宫格图片控件

2,245 stars409 forksJavaLicense varies

At a glance

What is it?
This is a Chinese-language Android gallery picker with multi-select, camera capture, long-image preview and the nine-grid layout from a chat composer. Its API returns a list of file path strings, it ships a modified copy of another library's source and tells you not to add the original, and its most recent tagged release is from 2016 while the repository was pushed to in July 2026.
Who is it for?
Adopt BGAPhotoPicker-Android only in a codebase that already depends on it and already targets a platform version where a path-based gallery API still works, because its result type is a list of path strings obtained through a storage permission.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository last received commits 81 days ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Ten years of commits, two tags from one afternoon

The version history needs reading carefully, because the two halves of it point in opposite directions.

There are two releases, both on 2016-07-01, a minute apart. They are the first and last tagged releases this repository has. Then there is a last push dated 2026-07-11, so somebody has been committing inside the last three months.

What that combination means is not abandonment and not activity, it is an unversioned library. The commits are there; the releases stopped a decade ago. For a component that people depend on through a package coordinate, that is a specific and uncomfortable position, because there is nothing to pin to and no changelog to read when something changes underneath you.

The install instructions lean into that rather than fighting it. The dependency line does not name a version at all:

groovy
    implementation 'com.github.bingoogolapple:BGAPhotoPicker-Android:latestVersion'
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    implementation 'androidx.recyclerview:recyclerview:1.0.0'
    implementation 'com.github.bingoogolapple:BGABaseAdapter-Android:2.0.1'

The placeholder is explained: it means whatever version the badge on the package page currently shows, and you are told to replace it yourself. So the intended workflow is to look up the latest build at install time. That works right up until you want two people on your team to have the same build, which is the moment the placeholder becomes a problem.

Two smaller signals in the same paragraph. The supporting libraries are pinned to versions from 2018, which is the era of the last release. And the block is labelled as four mandatory libraries while listing five, which is the kind of drift a decade of untagged edits produces.

A vendored, modified fork, with an instruction not to add the original

The single most consequential line in the setup section is a prohibition.

The README explains that supporting long-image preview required bringing in the source of a popular image zoom library and modifying it, and that because of this you must not add that library to your own project as well.

That is a vendored fork with a source-level modification, and the consequences are worth stating in order of severity. The fork is frozen at whatever version was current when it was copied, so it cannot receive upstream fixes, and there is no version number telling you which. If another library in your application also depends on the original, you now have two copies of the same classes with different behaviour, and the instruction does not address that case at all. And you cannot tell from your dependency tree what you have, because the fork is inside the library's own source rather than declared as a dependency.

The pattern is common in Android libraries of that generation, and the reason is not laziness. Android library dependencies are compiled into the application, so two versions of the same class in one process is a runtime crash rather than a warning, and for a long-press zoom view the modification needed is small enough that vendoring seemed cheaper than forking, publishing and asking every consumer to add a repository.

But it is also the thing that most often makes a library like this impossible to upgrade, and it interacts with the version story from the previous section. A component whose own dependency is a frozen fork and whose releases stopped in 2016 is a component you stay on, not one you upgrade.

If you are reading this to decide whether to adopt it, that is the line to read twice.

Four image loaders, and one that ignores a documented option

The library does not load images. It delegates that entirely to whichever of four image loading libraries you choose, and requires you to pick exactly one.

The active line in the dependency block is one of the modern pair, with its annotation processor declared separately. The other three are present but commented out, which is the right way to show the choice, and the comment above them says you must select one of the four.

That design is genuinely good for a component library. Image loading is the dependency every Android project has an opinion about, and a picker that forces one would be a picker you work around. Delegating means you can keep your existing loader, and it means the library's own behaviour is not coupled to a load strategy.

The cost is that there are four code paths through the same screens, and one of them does not do what the documentation says. The pause-on-scroll option is described as configurable: pause loading while the list scrolls and resume when it stops. The same line notes that this configuration has no effect when the fourth loader is used. So there is a documented feature with a documented exception, and neither is discoverable until you have already picked that loader.

The compatibility note at the top of the README is about version 3 of the active loader, while the dependency block pins version 4. That is not necessarily wrong, since a fix for one version is often a fix for the other, but it means the headline compatibility claim and the pinned version are talking about different things and you have to work out which applies.

Small detail, but it dates the codebase precisely: the annotation processor for that loader is declared with the old annotation-processing mechanism rather than the newer plugin, and the deprecated support library for compatibility widgets is a mandatory dependency.

The result is a list of paths, and the permission is a storage permission

Look at what the API gives you back and the design tells you its age.

The result contract is a static accessor that takes the activity result and returns a list of strings, which are paths to image files on disk. The permission check asks for write access to external storage and, when the camera is wanted, the camera. And the picker reads the device gallery rather than asking the platform for a selection.

That was the correct implementation when it was written. It is also the pattern the platform has moved away from, and it is worth being concrete about why rather than waving at deprecation.

Scoped storage changed what write access to external storage means, and the modern permission model replaced the whole grant-a-bucket-of-files approach with a picker that hands your application a URI for the things the user chose. That is not only a permissions change; it is a change of result type. A list of path strings assumes your process can still open those paths, and on a current Android target it generally cannot, because the media the gallery returned belongs to another application and is no longer yours to read by path.

So the honest position is that this library predates that shift, and its result type is the thing that will not survive it. The repository's own topic list includes the modern system picker, which suggests the author is aware of where the platform went, and the last push is recent enough that something changed even without a release.

If you are evaluating this in a codebase that already works, that is context. If you are evaluating it for a new project on a current target, the result type alone is probably disqualifying, and no amount of care elsewhere in the library changes that.

Customisation by shadowing resources, with no typed theme

The styling section is short and the technique is old and reliable: override the resources.

You can add files with the same names into your own drawable directory to replace the library's images, and you can add colour resources with the same names into your own colour file. The library ships prefixed colour names so there is room to add them, and the example shows the picker exposing three: a status bar colour, a navigation bar colour and a toolbar colour, each aliased to your app's own values.

Resource shadowing works because Android resolves a resource name against the application's resources before the library's. It has one significant advantage, which is that it needs no API, no theme object and no rebuild of the library, and one significant disadvantage, which is that it is entirely untyped. Misspell a colour name and your value is simply never consulted; there is no compile error and no warning, the screen just keeps the default.

That is the trade every Android component of this era made, and the reason it disappeared is that the newer approach gives you a typed surface and a compile error. Resource names as a theme surface is workable when you know the names, and the list in the README is not complete, so the practical approach is to read the library's own resource file.

Two other things in the tree are worth noting for the same reason. The project is a root Gradle module plus a library module plus a demo module with its own ProGuard rules, which is the shape of a library that was set up once and not restructured. And the demo is distributed through a URL shortener with a QR code in the README rather than through a repository release or an app listing, which was the common pattern of the period and is now a link that may not resolve.

Builders, activities and results, which is the whole API shape

There are three activities in this library and they are configured the same way, so learning one is learning all three.

The picker takes a builder on the activity's intent with a set of options: where to store a photo taken by the camera, with the documented behaviour that passing nothing disables the camera inside the gallery; the maximum number of selections, which the demo computes as a remaining budget; the paths already selected; and whether to pause loading while the list scrolls. It is started with the standard for-result call and the result is read through a static accessor.

The selection preview takes a similar builder: the paths to preview, the paths currently selected, a maximum count, the position to open at, and a flag for whether the user arrived from taking a photo, which matters because the preview then has to reconcile a freshly captured image with the existing selection.

The plain preview is smaller: a directory to save images into, with null disabling saving, then either one image or a list plus an index depending on how many you pass. That branching is the one place where the builder is doing real work rather than passing options through.

The permission pattern is the standard one of the period: an annotation on a method, an integer request code, a runtime permission library, and a message string in the user's language explaining which permissions are needed and why.

What is absent is as informative as what is present. There is no fragment API, so using this from a fragment or from a modern declarative UI means wrapping it. There is no asynchronous result callback. And the activity is where all the state lives, which means configuration changes, process death and multi-window are handled by the platform rather than by you, which is a trade that is acceptable exactly as long as the library still runs.

Where this is the right and wrong answer

The right answer: an existing application, on a target where path-based gallery access still works, that already has one of the four supported image loaders, and that needs the nine-grid layouts.

That last part is worth separating out, because it is the only part here that is genuinely hard to build and that has nothing to do with picking images. The library ships a nine-grid widget for a list, the layout used by chat posts, and a drag-to-reorder version of it for a composer. Multi-image layout with correct aspect-ratio handling and long-press reorder is a week of work that every messaging-shaped application needs and few want to write. Extracting that from a bigger SDK is the stated origin of the project, and it is a reasonable reason for it to exist.

The wrong answer: a new project. The reasons are cumulative rather than any single one. The result type is a list of paths, which is the thing the platform moved away from. The last tagged release is from 2016 and the install instructions resolve the version from a badge, so nobody can pin a build. The library ships a modified copy of a third-party source and tells you not to add the original, so two of your dependencies can disagree about the same class. And one of the four image loader paths silently ignores a documented configuration option.

There is also the community signal. The support path in the README is issues, pull requests, an email address, a group chat number and a tip jar. That was a complete and effective support model for a library in 2016. It is not evidence of anything now.

So the practical advice is narrow. Read the source if you already depend on it, because you have to know what you have. Reimplement the grid layouts if that is what you came for. And if you need a picker, use the one the platform now provides.

Editorial conclusion

Adopt BGAPhotoPicker-Android only in a codebase that already depends on it and already targets a platform version where a path-based gallery API still works, because its result type is a list of path strings obtained through a storage permission. Do not start a new project on it: the last tagged release is from 2016-07-01, the install instructions defer the version to a badge on a jitpack page, and the library ships a modified copy of PhotoView's source so that dependency cannot be upgraded. If you need a picker today the platform's own avoids both problems, and if it is the nine-grid widgets you want, those are the part worth reimplementing.

Frequently asked questions

What does BGAPhotoPicker-Android provide?

An Android gallery picker with single and multi image selection, camera capture, selection preview and image preview with zoom, square and circular avatar widgets, a nine-grid image layout for a chat-style list, and a drag-to-reorder nine-grid layout for a composer. It also offers a helper for cropping and capturing photos.

How do I add BGAPhotoPicker-Android to an Android project?

Add the jitpack repository to the repositories in your root build script, then add the dependency in your application script. The README shows the placeholder for the version rather than a number, telling you to take it from the badge on the package page and replace it yourself, plus four supporting libraries and one image loading library of your choice.

Which image loading libraries does BGAPhotoPicker-Android support?

Four: two of the widely used modern ones plus two older ones, and you must add exactly one of them to your own dependencies because the library does not load images itself. The README notes that the pause-image-loading-while-scrolling option has no effect with one of the four.

Can I use the PhotoView library alongside BGAPhotoPicker-Android?

No. The README states that the library has already brought in and modified PhotoView's source in order to support long-image preview, and that your project should not add PhotoView again. That means the dependency is a frozen fork inside the library's own source, so it cannot be upgraded and may conflict with another dependency of yours.

What permissions does BGAPhotoPicker-Android request?

Write access to external storage for reading the gallery, and the camera when the capture feature is used. The picker checks them with a runtime permission library and shows a message listing which permissions are needed and why, from a method annotated to run once the grant is confirmed.

How do I customise the appearance of BGAPhotoPicker-Android?

By overriding its resources. Add files with the same names in your own drawable directory to replace the library's images, and add colour resources with the same names to your colour file. The library ships prefixed colour names such as one for the status bar, one for the navigation bar and one for the toolbar, each aliased to your own values.

Official sources

  1. bingoogolapple/BGAPhotoPicker-Android on GitHub
  2. Issues
  3. README
  4. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/bingoogolapple-bgaphotopicker-android.svg)](https://hysenlabs.com/projects/bingoogolapple-bgaphotopicker-android)