Android Intents Tutorial with Kotlin

In this Intents tutorial you’ll learn what Intents are, the role they play in Android, and how to use them to communicate with other installed apps. By Jenn Bailey.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

Explicit Intents

It’s nearly time to build phase two of your meme generator. But first you need to get your picture over to the next activity since you’re a little strapped for screen real estate here.

In Constants.kt, add the following constants just below the comment line:

const val IMAGE_URI_KEY = "IMAGE_URI"
const val BITMAP_WIDTH = "BITMAP_WIDTH"
const val BITMAP_HEIGHT = "BITMAP_HEIGHT"

These will be used as keys for the extras you’ll pass to an intent on the next screen.

Now, add the following method to the bottom of TakePictureActivity, adding any imports as necessary:

private fun moveToNextScreen() {
  if (pictureTaken) {
    val nextScreenIntent = Intent(this, EnterTextActivity::class.java).apply {
      putExtra(IMAGE_URI_KEY, selectedPhotoPath)
      putExtra(BITMAP_WIDTH, pictureImageview.width)
      putExtra(BITMAP_HEIGHT, pictureImageview.height)
    }
    startActivity(nextScreenIntent)
  } else {
    Toaster.show(this, R.string.select_a_picture)
  }
}

Here you check pictureTaken to see if it’s true, which indicates your ImageView has a Bitmap from the camera. If you don’t have a Bitmap, then your activity will briefly show a Toast message telling you to go take a photo. The show method from the Toaster class makes showing toasts just a tiny bit easier. If pictureTaken is true, then you create an intent for the next activity and set up the necessary extras, using the constants you just defined as the keys.

Next, in the onClick() function, replace the empty closure in the when statement for the R.id.enter_text_button branch condition with a call to the moveToNextScreen() function. The resulting line of code should look like the following:

R.id.enterTextButton -> moveToNextScreen()

Build and run. Tap LETS MEMEIFY! without first taking a photo and you’ll see the toast appear:

Toast Message Appears

If a photo is taken, then moveToNextScreen() proceeds to create an intent for the text entry activity. It also attaches some Extras to the intent, such as the Uri path for the Bitmap and the height and width of the Bitmap as it’s displayed on the screen. These will be useful in the next activity.

You’ve just created your first explicit intent. Compared to implicit intents, explicit intents are a lot more conservative. This is because they describe a specific component that will be created and used when the intent starts. This could be another activity that is a part of your app or a specific Service in your app, such as one that starts to download a file in the background.

This intent is constructed by providing the Context from which the intent was created, this, along with the class the intent needs to run, EnterTextActivity::class.java. Since you’ve explicitly stated how the intent gets from A to B, Android simply complies. The user has no control over how the intent is completed:

intent_activity

Build and run. Repeat the process of taking a photo, but this time tap LETS MEMEIFY!. Your explicit intent will kick into action and take you to the next activity:

11. Enter Text Activity

The starter project already has this activity created and declared in AndroidManifest.xml, so you don’t have to create it yourself.

Handling Intents

Looks like that intent worked like a charm. But where are those Extras you sent across? Did they take a wrong turn at the last memory buffer? Time to find them and put them to work.

Add the following code at the end of onCreate() in EnterTextActivity.kt:

pictureUri = intent.getParcelableExtra<Uri>(IMAGE_URI_KEY)
val bitmapWidth = intent.getIntExtra(BITMAP_WIDTH, 100)
val bitmapHeight = intent.getIntExtra(BITMAP_HEIGHT, 100)

pictureUri?.let {
  val selectedImageBitmap = BitmapResizer.shrinkBitmap(this, it, bitmapWidth, 
    bitmapHeight)
  selectedPictureImageview.setImageBitmap(selectedImageBitmap)
}

When you create the activity, you assign the Uri passed from the previous activity to pictureUri by accessing the Intent via intent. Once you have access to the intent, you can access its Extra values.

Since variables and objects come in various forms, you have multiple methods to access them from the intent. To access the Uri object above, for example, you need to use getParcelableExtra(). Other Extra methods exist for other variables such as strings and primitive data types.

getIntExtra(), similar to other methods that return primitives, also allows you to define a default value. These are used when a value isn’t supplied, or when the key is missing from the provided Extras.

Once you’ve retrieved the necessary Extras, create a Bitmap from the Uri sized by the BITMAP_WIDTH and BITMAP_HEIGHT values you passed. Finally, you set the ImageView image source to the bitmap to display the photo.

In addition to displaying the ImageView, this screen also contains two EditText views where the user can enter their meme text. The starter project does the heavy lifting for you by taking the text from those views and compositing it onto the photo.

The only thing you need to do is to flesh out onClick(). Update the line to the R.id.write_text_to_image_button branch condition:

R.id.writeTextToImageButton -> createMeme()

Drumroll please. Build and run. Repeat the usual steps to take a photo, and then enter your incredibly witty meme text on the second screen and tap LETS MEMEIFY!:

Image Memeified

You’ve just created your own meme generator! Don’t celebrate too long, though: There are a few bits of polish that you need to add to the app.

Broadcast Intents

It would be nice to save your shiny new meme so you can share it with the world. It’s not going to go viral all on its own! :]

Fortunately, the starter project has it covered for you — you only need to tie things together.

Add the following code to saveImageToGallery(), just below the try block before Toaster.show(this, R.string.save_image_succeeded):

val mediaScanIntent = Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE)
mediaScanIntent.data = Uri.fromFile(imageFile)
sendBroadcast(mediaScanIntent)

This intent uses the ACTION_MEDIA_SCANNER_SCAN_FILE action to ask the Android’s media database to add the image’s Uri. That way, any apps that access the media database can use the image via its Uri.

The ACTION_MEDIA_SCANNER_SCAN_FILE action also requires the intent to have some attached data in the form of a Uri, which comes from the File object to which you save the Bitmap.

Finally, you broadcast the intent across Android so that any interested parties — in this case, the media scanner — can act upon it. Since the media scanner doesn’t have a user interface, you can’t start an activity so you simply broadcast the intent instead.

Now, update the R.id.save_image_button branch condition in the onClick() function to the following:

R.id.saveImageButton -> askForPermissions()

When the user taps SAVE IMAGE the above code checks for WRITE_EXTERNAL_STORAGE permission. If it’s not granted on Android Marshmallow and above, the method politely asks the user to grant it. Otherwise, if you are allowed to write to the external storage, it simply passes control to saveImageToGallery().

The code in saveImageToGallery() performs some error handling and, if everything checks out, kicks off the intent.

Build and run. Take a photo, add some stunningly brilliant meme text, tap LETS MEMEIFY! and then tap SAVE IMAGE once your image is ready.

Now, close the app and open the Photos app. If you’re using the emulator, then open the Gallery app. You should be able to see your new image in all its meme-ified glory:

image from photos

Your memes can now escape the confines of your app and are available for you to post to social media or share in any manner of your choosing. Your meme generator is complete!