Android In App Review

Jan 28 2021 · Kotlin 1.4, Android 5, Android Studio 4

Part 1: Implementing In App Review

04. Store Review Preferences

Episode complete

Play next episode

Next
About this episode
Leave a rating/review
See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 03. Build An In App Review Module Next episode: 05. Provide Dependencies

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Notes: 04. Store Review Preferences

The UI code for the In App Review feature is predefined for you, as you’ll focus on the technical aspects of IAR. You’re free to explore the UI logic and code to learn more.

Make sure you use the starting project from this episode and that you set it up in the same way you did in the ‘Set Up The Project’ episode.

Transcript: 04. Store Review Preferences

Now that you have the module set up, you can start working on the In App Review feature. Before you dive into writing code, take a moment to look at the dialog package and its contents. The UI part of the code is predefined for you, as it’s less important than the business logic and the Review flow code you’ll work on.

You’ll show the dialog only after a user finishes a course, or opens a course they’ve already finished. This is great because you want to have users in a good mood, before asking them to do anything in your app. You’ll learn exactly how this is better once you start testing the feature, later in the course.

The dialog will show two options - one to rate the app and another to rate later. The first option will start the review flow and save the user’s choice to preferences, so you don’t ask them again. The second option will close the dialog and again save the user’s option to preferences, with the timestamp of when this happened. That way, you can ask the user again in two weeks. This is pretty common behavior that most apps implement.

Now that you’ve learned what the dialog will do, let’s start writing code to support its behavior. Create a new package named preferences and add a new interface to the package, named InAppReviewPreferences, with the following code:

interface InAppReviewPreferences {

  fun hasUserRatedApp(): Boolean

  fun setUserRatedApp(hasRated: Boolean)

  fun hasUserChosenRateLater(): Boolean

  fun setUserChosenRateLater(hasChosenRateLater: Boolean)

  fun getRateLaterTime(): Long

  fun setRateLater(time: Long)
}

This interface will serve as an abstraction behind the logic to store and fetch data. The functions you added will let you know if the user already rated the app or if they chose to rate later. It will also let you know what later means, as you’ll store a timestamp to know when to ask the user again.

Now add a class to the package, named InAppReviewPreferencesImpl and add the following code:

class InAppReviewPreferencesImpl @Inject constructor(
  private val sharedPreferences: SharedPreferences
) : InAppReviewPreferences {

  companion object {
    private const val KEY_HAS_RATED_APP = "hasRatedApp"
    private const val KEY_CHOSEN_RATE_LATER = "rateLater"
    private const val KEY_RATE_LATER_TIME = "rateLaterTime"
  }
}

To start off, you implemented the InAppReviewPreferences interface and you added SharedPreferences as the constructor parameter. You need somewhere to write to and read from the user’s choices. The preferences will help you do that.

You also added appropriate keys for your data, that represent if the user rated the app, if they chose to rate later and when you should ask for a review again. All of this should be straightforward, so proceed to implement interface functions. Add the following code:

  override fun hasUserRatedApp(): Boolean =
    sharedPreferences.getBoolean(KEY_HAS_RATED_APP, false)

  override fun setUserRatedApp(hasRated: Boolean): Unit =
    sharedPreferences.edit { putBoolean(KEY_HAS_RATED_APP, hasRated) }
    
  override fun hasUserChosenRateLater(): Boolean =
    sharedPreferences.getBoolean(KEY_CHOSEN_RATE_LATER, false)

  override fun setUserChosenRateLater(hasChosenRateLater: Boolean): Unit =
    sharedPreferences.edit { putBoolean(KEY_CHOSEN_RATE_LATER, hasChosenRateLater) }

  override fun getRateLaterTime(): Long =
    sharedPreferences.getLong(KEY_RATE_LATER_TIME, System.currentTimeMillis())

  override fun setRateLater(time: Long): Unit =
    sharedPreferences.edit { putLong(KEY_RATE_LATER_TIME, time) }

Again, all of this should make sense out of the box. All these functions do is store data within SharedPreferences and read the data from those preferences. Do notice how you used the edit() function, which takes in only a lambda function. This extension function is a part of the core KTX dependency and it makes it easier to use SharedPreferences.

Now that you’ve implemented the preferences and the data part of the feature, you can proceed to connect this to your review dialog. Open the InAppReviewPromptDialog and add the following property at the top of the class:

  @Inject
  lateinit var preferences: InAppReviewPreferences

Using Hilt, you’ll inject the preferences wrapper you just built and communicate any of the two options the user chooses to them. You’ll provide the dependency through Hilt in the next episode. Now change the onLeaveReviewTapped() function to the following:

  private fun onLeaveReviewTapped() {
    preferences.setUserRatedApp(true)
    // TODO start review
    dismissAllowingStateLoss()
  }

When the user taps the leave review button, you’ll store their choice. You’ll also start the review flow, but in a later episode. Now change the onRateLaterTapped() function:

  private fun onRateLaterTapped() {
    preferences.setUserChosenRateLater(true)
    preferences.setRateLater(getLaterTime())
    dismissAllowingStateLoss()
  }

Alternatively, you’ll store that the user chose to rate later, and what time that is, if they decide to rate the app at a later time. You still need to implement the cancel function and the getLaterTime() function, so do that the following way:

  override fun onCancel(dialog: DialogInterface) {
    preferences.setUserChosenRateLater(true)
    preferences.setRateLater(getLaterTime())
    super.onCancel(dialog)
  }

  private fun getLaterTime(): Long {
    return System.currentTimeMillis() + TimeUnit.DAYS.toMillis(14)
  }

You could extract the cancel and later button code to another function, given that in both cases you’d just leave the user to use the app and ask them to rate the app at another time.

That’s it! Awesome job! You’ll provide all the necessary dependencies in the next episode, to make this piece of code work.