Fallery — A fully customizable media picker for Android

Mehdi Yari
4 min readSep 12, 2020
https://github.com/mehdiyari/Fallery

Fallery is a customizable media picker for Android. Fallery is compatible with Android API Levels +14 and has many features.

Key features

1. Select media(photo, video) from android media store with
custom [Offline Gallery](http://mehdiyari.ir/2020/08/14/create-a-custom-offline-online-gallery-with-fallery/)
or
custom [Online Gallery](http://mehdiyari.ir/2020/08/14/create-a-custom-offline-online-gallery-with-fallery/)
2. Compatible with Android API Level +14
3. Content Observer(When a new media file is added to the device, fallery is informed and shows new
media in the UI)
4. Filter media based on types(Photos, videos, both)
5. Modern user interface with the capability to add new themes and languages
6. Select media with a text caption
7. Taking photos from the camera with intent
8. Show bucket list in two different UI(Grid and linear), which user can switch between them in
runtime
9. Support max selectable media
10. Support screen orientation
11. Support custom edits text layout for the text caption(for emoji compatibility, etc.)
12. Support turning on or off media counts in the fallery toolbar
13. Support vertical and horizontal scrolling in showing media view-pager
14. Support custom view-pager transformer in media view-pager
15. Support custom action for video toggle in the video preview screen.
16. Support requests external storage permissions for all Android versions automatically.
17. Support changing the row count of images based on user zoom-in or zoom-out.

Usage

How to add Fallery to your projects?

Gradle. Step 1. Add it in your root build.gradle at the end of repositories:

allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}

Step 2. Add the dependency

dependencies {
implementation 'com.github.mehdiyari:Fallery:{latest_version}'
}

Maven

<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependency>
<groupId>com.github.mehdiyari</groupId>
<artifactId>Fallery</artifactId>
<version>{latest_version}</version>
</dependency>

How to use Fallery to pick media on your Android device?

Fallery is designed to be used in Java and kotlin. But before starting gallery, you must set an image loader because fallery does not use any image loader libraries by default. To create a fallery image loader, you must create an Image Loader and implement the FalleryImageLoader interface.

class GlideImageLoader : FalleryImageLoader {

override fun loadPhoto(
context: Context,
imageView: ImageView,
resizeDiminution: PhotoDiminution,
placeHolderColor: Int,
path: String
) {
Glide.with(imageView)
.asBitmap()
.placeholder(ColorDrawable(placeHolderColor))
.load(path)
.override(resizeDiminution.width, resizeDiminution.height)
.into(imageView)
}

override fun loadGif(
context: Context,
imageView: ImageView,
resizeDiminution: PhotoDiminution,
placeHolderColor: Int,
path: String
) {
Glide.with(imageView)
.asGif()
.placeholder(ColorDrawable(placeHolderColor))
.load(path)
.override(resizeDiminution.width, resizeDiminution.height)
.into(imageView)
}
}

And simply passing the new object of this class to FalleryBuilder.

Kotlin

class MainActivity : AppCompatActivity(R.layout.activity_main) {

private val glideImageLoader by lazy { GlideImageLoader() }

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

val falleryOptions = FalleryBuilder()
.setImageLoader(glideImageLoader)
.build()

falleryButton.setOnClickListener {
startFalleryWithOptions(requestCode = 1, falleryOptions = falleryOptions)
}
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 1 && resultCode == RESULT_OK && data != null) {
handleResultWithCaption(
result = data?.getFalleryResultMediasFromIntent(),
caption = data?.getFalleryCaptionFromIntent()
)
}
}

private fun handleResultWithCaption(result: Array<String>?, caption: String?) {
TODO("handle result and caption")
}
}

Java

public class MainActivity extends AppCompatActivity {

private Button button;
private GlideImageLoader glideImageLoader = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
glideImageLoader = New GlideImageLoader();

final FalleryOptions falleryOptions = new FalleryBuilder()
.setImageLoader(glideImageLoader)
.build();

this.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Fallery.startFalleryFromActivityWithOptions(
MainActivity.this, 1, falleryOptions
);
}
});
}

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 1 && resultCode == RESULT_OK && data != null) {
String[] result = Fallery.getResultMediasFromIntent(data);
String caption = Fallery.getCaptionFromIntent(data);
handleResultWithCaption(result, caption);
}
}

private void handleResultWithCaption(String[] result, String caption) {
// todo: handle result and caption
}
}

These examples show how to start fallery in Java/kotlin activity. For Fragments, in kotlin, you can start fallery with startFalleryWithOptions but for Java, you must start fallery with Fallery.startFalleryFromFragmentWithOptions and override onActivityResult method for fragments and extracting the result from input intent.

Create a custom offline/online gallery with Fallery

As an Android developer, in many scenarios, we need our app user to select media from the media on the media store, internet, assets, or cache dir. Fallery offers a solution to this problem.

Online and Offline Media Picker

Fallery has two interfaces for providing data AbstractMediaBucketProviderandAbstractBucketContentProvider. You can extend these two interfaces to provide your data to Fallery instead of the Android media store.

class CustomOnlineBucketProvider : AbstractMediaBucketProvider {

override suspend fun getMediaBuckets(bucketType: BucketType): List<MediaBucket> {
// get buckets list from any datasource
}
}
class CustomOnlineBucketContentProvider : AbstractBucketContentProvider {

override suspend fun getMediasOfBucket(
bucketId: Long,
bucketType: BucketType
): Flow<List<Media>> {
// get bucket content by bucketId from any datasource
}
}

And simply passing concrete classes to FalleryBuilder

FalleryBuilder()
.setImageLoader(glideImageLoader)
.setContentProviders(CustomOnlineBucketContentProvider(), CustomOnlineBucketProvider())
.setMediaObserverEnabled(false)
.setGrantExternalStoragePermission(false)
.build()

You can find out more examples in the fallery example app on GitHub.

Contribute

I would appreciate your contributions to PRs, wiki, issues, etc.

Follow me on Medium and GitHub if you are interested in my articles.

--

--