Adding TextDrawable as a PlaceHolder in Open Event Android App

The Open Event Android project has a fragment for showing speakers of the event. Each Speaker model has image-url which is used to fetch the image from server and load in the ImageView. In some cases it is possible that image-url is null or client is not able to fetch the image from the server because of the network problem. So in these cases showing Drawable which contains First letters of the first name and the last name along with a color background gives great UI and UX. In this post I explain how to add TextDrawable as a placeholder in the ImageView using TextDrawable library.

1. Add dependency

In order to use TextDrawable in your app add following dependencies in your app module’s build.gradle file.

dependencies {
	compile 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
}

2. Create static TextDrawable builder

Create static method in the Application class which returns the builder object for creating TextDrawables. We are creating static method so that the method can be used all over the App.

private static TextDrawable.IShapeBuilder textDrawableBuilder;

public static TextDrawable.IShapeBuilder getTextDrawableBuilder()
 {
        if (textDrawableBuilder == null) {
            textDrawableBuilder = TextDrawable.builder();
        }
        return textDrawableBuilder;
}

This method first checks if the builder object is null or not and then initialize it if null. Then it returns the builder object.

3.  Create and initialize TextDrawable object

Now create a TextDrawable object and initialize it using the builder object. The Builder has methods like buildRound(), buildRect() and buildRoundRect() for making drawable round, rectangle and rectangle with rounded corner respectively. Here we are using buildRect() to make the drawable rectangle.

TextDrawable drawable = OpenEventApp.getTextDrawableBuilder()
                    .buildRect(Utils.getNameLetters(name), ColorGenerator.MATERIAL.getColor(name));

The buildRect() method takes two arguments one is String text which will be used as a text in the drawable and second is int color which will be used as a background color of the drawable. Here ColorGenerator.MATERIAL returns material color for given string.

4.  Create getNameLetters()  method

The getNameLetters(String name) method should return the first letters of the first name and last name as String.

Example, if the name is “Shailesh Baldaniya” then it will return “SB”.

public static String getNameLetters(String name) {
        if (isEmpty(name))
            return "#";

        String[] strings = name.split(" ");
        StringBuilder nameLetters = new StringBuilder();
        for (String s : strings) {
            if (nameLetters.length() >= 2)
                return nameLetters.toString().toUpperCase();
            if (!isEmpty(s)) {
                nameLetters.append(s.trim().charAt(0));
            }
        }
        return nameLetters.toString().toUpperCase();
}

Here we are using split method to get the first name and last name from the name. The charAt(0) gives the first character of the string. If the name string is null then it will return “#”.   

5.  Use Drawable

Now after creating the TextDrawable object we need to load it as a placeholder in the ImageView for this we are using Picasso library.

Picasso.with(context)
        .load(image-url)
        .placeholder(drawable)
        .error(drawable)
        .into(speakerImage);

Here the placeholder() method displays drawable while the image is being loaded. The error() method displays drawable when the requested image could not be loaded when the device is offline. SpeakerImage is an ImageView in which we want to load the image.

Conclusion

TextDrawable is a great library for generating Drawable with text. It has also support for animations, font and shapes. To know more about TextDrawable follow the links given below.

Binding Images Dynamically in Open Event Orga App

In Open Event Orga App (Github Repo), we used Picasso to load images from URLs and display in ImageViews. Picasso is easy to use, lightweight, and extremely configurable but there has been no new release of the library since 2015. We were using Picasso in binding adapters in order to dynamically load images using POJO properties in the layout XML itself using Android Data Binding. But this configuration was a little buggy.

The first time the app was opened, Picasso fetched the image but it was not applied to the ImageView. When the device was rotated or the activity was resumed, it loaded just fine. This was a critical issue and we tried many things to fix it but none of it quite fit our needs. We considered moving on to other Image Loading libraries like Glide, etc but it was too heavy on the size and functionality for our needs. The last resort was to update the library to develop version using Sonatype’s snapshots Repository. Now, the Picasso v2.6.0-SNAPSHOT is very stable but not released to the maven central repository, and a newer develop version v3.0.0-SNAPSHOT was launched too. So we figured we should use that. This blog will outline the steps to include the develop version of Picasso, configuring it for our needs and making it work with Android Data Binding.

Setting up Dependencies

Firstly, we need to include the sonatype repository in the repositories block of our app/build.gradle

repositories {
   ...
   maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
}

 

Then we need to replace the Picasso dependency entry to this:

compile 'com.squareup.picasso:picasso:3.0.0-SNAPSHOT'

 

Note that if you used Jake Wharton’s OkHttp3 Downloader for Picasso, you won’t need it now, so you need to remove it from the dependency block

And you need to use this to import the downloader

import com.squareup.picasso.OkHttp3Downloader;

 

Next, we set up our Picasso DI this way

Picasso providesPicasso(Context context, OkHttpClient client) {
   Picasso picasso = new Picasso.Builder(context)
       .downloader(new OkHttp3Downloader(client))
       .build();
   picasso.setLoggingEnabled(true);
   return picasso;
}

 

Set the singleton instance in our application:

Picasso.setSingletonInstance(picasso);

 

And we are ready to use it.

Creating Adapters

Circular Image Adapter

We show event logos as circular images, so we needed to create a binding adapter for that:

@BindingAdapter("circleImageUrl")
public static void bindCircularImage(ImageView imageView, String url) {
   if(TextUtils.isEmpty(url)) {
       imageView.setImageResource(R.drawable.ic_photo_shutter);
       return;
   }

   Picasso.with()
       .load(Uri.parse(url))
       .error(R.drawable.ic_photo_shutter)
       .placeholder(R.drawable.ic_photo_shutter)
       .transform(new CircleTransform())
       .tag(MainActivity.class)
       .into(imageView);
}

 

If the URL is empty, we just show the default photo, and otherwise we load the image into the view using standard CircleTransform

Note that there is no context argument in the with method. This was implemented in Picasso recently where they removed the need for context for loading images. Now, they use a Dummy ContentProvider to get application context, which is inspired by how Firebase does it.

Now, we can just normally use this binding in layout to load the event thumbnail like this

<ImageView
   android:layout_width="@dimen/image_small"
   android:layout_height="@dimen/image_small"
   android:contentDescription="@string/event_thumbnail"
   app:circleImageUrl="@{event.thumbnailImageUrl}" />

 

This gives us a layout like this:

Next we need to load the header image with a deafult image.

Default Image Adapter

For this, we write a very simple adapter without CircleTransform

@BindingAdapter(value = { "imageUrl", "placeholder" }, requireAll = false)
public static void bindDefaultImage(ImageView imageView, String url, Drawable drawable) {
   if(TextUtils.isEmpty(url)) {
       if (drawable != null)
           imageView.setImageDrawable(drawable);
       return;
   }

   RequestCreator requestCreator = Picasso.with().load(Uri.parse(url));

   if (drawable != null) {
       requestCreator
           .placeholder(drawable)
           .error(drawable);
   }

   requestCreator
       .tag(MainActivity.class)
       .into(imageView);
}

 

As imageUrl or placeholder can be null, we check for both, and setting correct images if they are not. We use this in our header layout with both the url and default image we need to show:

<ImageView
   android:scaleType="centerCrop"
   app:imageUrl="@{ event.largeImageUrl }"
   app:placeholder="@{ @drawable/header }"
   android:contentDescription="@string/event_background" />

 

And this gives us a nice dynamic header like this:

This wraps up the blog on Picasso’s latest develop version and Binding Adapters. If you want to know more about Picasso and Android Data Binding, check these links:

Cache Thumbnails and Images Using Picasso in Open Event Android

In the event based Android projects like Open Event Android, we have speakers and sponsors. And these projects needs to display image of the speakers and sponsors because it affects project a lot. So instead of every time fetching image from the server it is good to store small images(thumbnails) in the cache and load images even if device is offline. It also reduces data usage.

Picasso is mostly used image loading library for Android. It automatically handles ImageView recycling and download cancellation in an adapter, complex image transformations with minimal memory use, memory and disk caching.

But one problem is Picasso caches images for only one session by default. I mean if you close the app then all by default cached image will be removed.  If you are offline then Picasso will not load cached images because of it. It will make network calls every time you open the app.

In this post I explain how to manually cache images using Picasso so that images load even if the device is offline. It will make a network call only once for a particular image and will cache image in memory.

We will use okhttp3 library for OkHttpClient.

1. Add dependency

In order to use Picasso in your app add following dependencies in your app module’s build.gradle file.

dependencies {
        compile 'com.squareup.okhttp3:okhttp:3.8.1'
        compile 'com.squareup.picasso:picasso:2.5.2'
}

2. Make static Picasso object

Make static Picasso object in the Application class so that we can use it directly from the other activity.

public static Picasso picassoWithCache;

3. Initialize cache

Create a File object with path as app specific cache and use this object to create a Cache object.

File httpCacheDirectory = new File(getCacheDir(), "picasso-cache");
Cache cache = new Cache(httpCacheDirectory, 15 * 1024 * 1024);

Here it will create a Cache object with 15MB. getCacheDir() method returns the absolute path to the application specific cache directory on the filesystem.

OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder().cache(cache);

4. Initialize Picasso with cache

Now initialize picassoWithCache object using Picass.Builder(). Set downloader for picasso by adding  new OkHttp3Downloader object.

picassoWithCache = new Picasso.Builder(this).downloader(new OkHttp3Downloader(okHttpClientBuilder.build())).build();

5. Use picassoWithCache object

As it is a static object you can directly use it from any activity. All the images loaded using this picassoWithCache instance will be cached in memory.

Application.picassoWithCache().load(thumbnail/image url);

 

To know more how i solved this issue in Open Event Project visit this link. To learn more about Picasso features follow the links given below.