Opening Orga App through Intent in Open Event Android App

In the Open Event Android App there is a section called “Manage events”, we direct the users to the Organizer’s app if the users have the app already installed otherwise we open the organizer’s app in the playstore. This way users are motivated to create events using the organizer’s app. Let’s see how this feature was implemented.

So when the user clicks on the menu item “Manage Events” startOrgaApp function is called with the package name of the organizer’s app as the argument. Android apps are uniquely identified by their package names in the playstore.

override fun onOptionsItemSelected(item: MenuItem?): Boolean {

when (item?.getItemId()) {
R.id.orgaApp -> {
startOrgaApp("org.fossasia.eventyay")
return true
}
}

 

Let’s have a look at the startOrgaApp function that we are calling above. We are using a try/catch block here. We are opening the app in the try block if it is installed otherwise we throw ActivityNotFoundException and if this exception happens we will catch it and use the showInMarket function to show the Organizer’s app in the market.

private fun startOrgaApp(packageName: String) {
val manager = activity?.packageManager
try {
val intent = manager?.getLaunchIntentForPackage(packageName)
?: throw ActivityNotFoundException()
intent.addCategory(Intent.CATEGORY_LAUNCHER)
startActivity(intent)
} catch (e: ActivityNotFoundException) {
showInMarket(packageName)
}
}

 

Now since we know this will never raise an exception there is no try catch block as the app with this package name will always be there. We just need to create an intent with the required parameters and then just pass the intent to startActivity

private fun showInMarket(packageName: String) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$packageName"))
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
}

 

Lastly we just need to add the XML code to create a menu item. It should contain the id so that we can reference it and the title that will be visible.

<group android:id="@+id/profileMenu">
<item
android:id="@+id/orgaApp"
android:title="@string/manage_events" />
</group>

 

That’s it now you can open an app in the playstore if it is not installed or just open the app if the user has already installed the app.  

Resources

  1. Vogella Intent tutorial – http://www.vogella.com/tutorials/AndroidIntent/article.html
  2. Official Android Documentation Intent – https://developer.android.com/guide/components/intents-filters
  3. Javatpoint intent tutorial – https://www.javatpoint.com/android-explicit-intent-example
Continue Reading

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:

Continue Reading
Close Menu