Skip to content

An Android library providing a dialog, which asks the user to rate the app or give feedback. You can also use the library to show the Google in-app review easily under certain conditions.

License

Notifications You must be signed in to change notification settings

SuddenH4X/awesome-app-rating

Repository files navigation

Awesome App Rating

Build Status

A highly customizable Android library providing a dialog, which asks the user to rate the app. If the user rates below the defined threshold, the dialog will show a feedback form or ask the user to mail his feedback. Otherwise it will ask the user to rate the app in the Google Play Store.

showcase

You can also use this library to show the Google in-app review easily under certain conditions:

In app review workflow for a user

(Source: https://developer.android.com/guide/playcore/in-app-review)

Features

  • Let the dialog (or the Google in-app review) show up at a defined app session, after n days of usage and/or if your custom conditions meet
  • Auto fetches the app icon to use it in the dialog
  • Ask the user to mail his feedback or show a custom feedback form if the user rates below the defined minimum threshold
  • All titles, messages and buttons are customizable
  • You can override all click listeners to fit your needs (or to implement extensive tracking)
  • The dialog handles orientation changes correctly
  • Extracts the accent color of your app's theme and works with dark/night theme out of the box

This library:

  • is completely written in Kotlin
  • is Unit tested
  • is optimized for MaterialComponent themes
  • supports Jetpack Compose
  • uses AndroidX
  • uses no third party dependencies
  • is easy debuggable
  • is Android 14 (API 34) ready
  • is easy to use

How to use

Gradle

The library supports API level 14 and higher. You can simply include it in your app via Gradle:

dependencies {
    ...
    implementation 'com.suddenh4x.ratingdialog:awesome-app-rating:2.7.0'
}

Since version 2.4.0 you can use the Maven Central repository:

allprojects {
    repositories {
		...
        mavenCentral()
    }
}

If you want to use an older version of this library, you have to use JCenter:

allprojects {
    repositories {
		...
        jcenter()
    }
}

Builder usage

This library provides a builder to configure its behavior.

 AppRating.Builder(this)
            .setMinimumLaunchTimes(5)
            .setMinimumDays(7)
            .setMinimumLaunchTimesToShowAgain(5)
            .setMinimumDaysToShowAgain(10)
            .setRatingThreshold(RatingThreshold.FOUR)
            .showIfMeetsConditions()

You should call the builder only in the onCreate() method of your main Activity class, because every call of the method showIfMeetsConditions will increase the launch times.

With the settings above the dialog will show up if the following conditions are met:

  • The app is installed for a minimum of 7 days and
  • the app is launched for a minimum of 5 times

Furthermore the dialog will show up again if the user has clicked the later button of the dialog and

  • The button click happened at least 10 days ago and
  • the app is launched again for a minimum of 5 times.

If the rate or never button is clicked once or if the user rates below the defined minimum threshold, the dialog will never be shown again unless you reset the library settings with AppRating.reset(this) - but doing this is not recommended.

If you have adjusted the dialog to suit your preferences, you have multiple possibilities to show it. Usually you want to show the dialog if the configured conditions are met:

ratingBuilder.showIfMeetsConditions()

This method also returns a boolean to indicate whether the dialog shows up or not. So you can prevent showing other dialogs at the same time as the rating dialog.

If you want you can also just create the dialog to show it later

ratingBuilder.create()

or you can show it immediately:

ratingBuilder.showNow()

Configuration

Between the constructor and the show or create method you can adjust the dialog to suit your preferences. You have the following options:

Google in-app review

If you want to use the in-app review from Google instead of the library dialog, call the following function:

.useGoogleInAppReview()

You can also add a completeListener which gets called if the in-app review flow has been completed. The boolean indicates if the flow was started correctly, but not if the in-app review was displayed to the user.

.setGoogleInAppReviewCompleteListener(googleInAppReviewCompleteListener: (Boolean) -> Unit)

Note: After the first in-app review flow was completed successfully the toShowAgain conditions will be used. For example .setMinimumLaunchTimesToShowAgain(launchTimesToShowAgain: Int) instead of .setMinimumLaunchTimes(launchTimes: Int).

Current issues with in-app review

Testing the Google in-app review isn't as easy as it should be. There is an open issue in the issuetracker of Google: https://issuetracker.google.com/issues/167352813

Follow these tips on stackoverflow to maximize your chance of testing it successfully:

- Use only one account in the device
- Ensure that account has installed the app (appears in the app & games > Library section in Play Store)
- The account is a GMAIL one, not a GSuit
- You can review with the account if you go to the app play listing page.
- The account has not reviewed
- If you intend to use the Internal Test Track ensure the account has joined the test track.
- When switching between different accounts and testing things out, sometimes might be helpful to "Clear Data" from the Play Store app.
- Try all the above with different account

Source: https://stackoverflow.com/a/63950373

When to show up

  • Change the number of days the app has to be installed
.setMinimumDays(minimumDays: Int) // default is 3
  • Change the minimum number of app launches
.setMinimumLaunchTimes(launchTimes: Int) // default is 5
  • Change the number of days that must have passed away after the last later button click
.setMinimumDaysToShowAgain(minimumDaysToShowAgain: Int) // default is 14
  • Change the minimum number of app launches after the last later button click
.setMinimumLaunchTimesToShowAgain(launchTimesToShowAgain: Int) // default is 5
  • Set a custom condition which will be evaluated before showing the dialog. See below for more information.
.setCustomCondition(customCondition: () -> Boolean)
  • Set a custom condition which will be evaluated before showing the dialog after the later button has been clicked. See below for more information.
.setCustomConditionToShowAgain(customConditionToShowAgain: () -> Boolean)
  • Disable app launch counting for this time. It makes sense to combine this option with the custom condition(s).
.dontCountThisAsAppLaunch()

Design

The following settings will only take effect if the library dialog is used (and not the Google in-app review).

General
  • Change the icon of the dialog
.setIconDrawable(iconDrawable: Drawable?) // default is null which means app icon
  • Change the theme of the dialog
.setCustomTheme(customTheme: Int)
  • Change the rate later button text
.setRateLaterButtonTextId(rateLaterButtonTextId: Int)
  • Add a click listener to the rate later button
.setRateLaterButtonClickListener(rateLaterButtonClickListener: RateDialogClickListener)
  • Show the rate never button, change the button text and add a click listener
.showRateNeverButton(rateNeverButtonTextId: Int, rateNeverButtonClickListener: RateDialogClickListener) // by default the button is hidden
  • Show the rate never button after n times(, change the button text and add a click listener). This means the user has to click the later button for at least n times to see the never button.
.showRateNeverButtonAfterNTimes(rateNeverButtonTextId: Int, rateNeverButtonClickListener: RateDialogClickListener, countOfLaterButtonClicks: Int)
Rating Overview
  • Change the title of the rating dialog
.setTitleTextId(titleTextId: Int)
  • Add a message to the rating dialog
.setMessageTextId(messageTextId: Int) // by default no message is shown
  • Change the confirm button text
.setConfirmButtonTextId(confirmButtonTextId: Int)
  • Add a click listener to the confirm button
.setConfirmButtonClickListener(confirmButtonClickListener: ConfirmButtonClickListener)
  • Show only full star ratings
.setShowOnlyFullStars(showOnlyFullStars: Boolean)  // default is false
Store Rating
  • Change the title of the store rating dialog
.setStoreRatingTitleTextId(storeRatingTitleTextId: Int)
  • Change the message of the store rating dialog
.setStoreRatingMessageTextId(storeRatingMessageTextId: Int)
  • Change the rate now button text
.setRateNowButtonTextId(rateNowButtonTextId: Int)
  • Overwrite the default rate now button click listener
.overwriteRateNowButtonClickListener(rateNowButtonClickListener: RateDialogClickListener) // by default it opens the Play Store listing of your app
  • Add an additional click listener to the rate now button (e.g. for extensive tracking while still using the default library behaviour)
.setAdditionalRateNowButtonClickListener(additionalRateNowButtonClickListener: RateDialogClickListener)
Feedback
  • Change the title of the feedback dialog
.setFeedbackTitleTextId(feedbackTitleTextId: Int)
  • Change the no feedback button text
.setNoFeedbackButtonTextId(noFeedbackButtonTextId: Int)
  • Add a click listener to the no feedback button
.setNoFeedbackButtonClickListener(noFeedbackButtonClickListener: RateDialogClickListener)
  • Use the custom feedback dialog instead of the mail feedback dialog
.setUseCustomFeedback(useCustomFeedback: Boolean) // default is false
Mail Feedback

If custom feedback is enabled, these settings will be ignored:

  • Change the message of the mail feedback dialog
.setMailFeedbackMessageTextId(feedbackMailMessageTextId: Int)
  • Set the mail settings for the mail feedback dialog (mail address, subject, text and error toast message)
.setMailSettingsForFeedbackDialog(mailSettings: MailSettings)
  • Change the mail feedback button text
.setMailFeedbackButtonTextId(mailFeedbackButtonTextId: Int)
  • Overwrite the mail settings with a custom click listener
.overwriteMailFeedbackButtonClickListener(mailFeedbackButtonClickListener: RateDialogClickListener)
  • Add an additional click listener to the mail feedback button (e.g. for extensive tracking while still using the default library behaviour)
.setAdditionalMailFeedbackButtonClickListener(additionalMailFeedbackButtonClickListener: RateDialogClickListener)
Custom Feedback

These settings will only apply if custom feedback is enabled:

  • Change the message of the custom feedback dialog
.setCustomFeedbackMessageTextId(feedbackCustomMessageTextId: Int)
  • Change the custom feedback button text
.setCustomFeedbackButtonTextId(customFeedbackButtonTextId: Int)
  • Add a click listener to the custom feedback button
.setCustomFeedbackButtonClickListener(customFeedbackButtonClickListener: CustomFeedbackButtonClickListener)

Other settings

  • Choose the rating threshold. If the user rates below, the feedback dialog will show up. If the user rates the threshold or higher, the store dialog will show up.
.setRatingThreshold(ratingThreshold: RatingThreshold) // default is RatingThreshold.THREE
  • Choose if the dialogs should be cancelable (by clicking outside or using the back button). This case is treated the same as a click on the later button.
.setCancelable(cancelable: Boolean) // default is false
  • Add a cancel listener to the dialog
.setDialogCancelListener(dialogCancelListener: () -> Unit)
  • Disable all library logs
.setLoggingEnabled(isLoggingEnabled: Boolean) // default is true
  • Enable debug mode, which will cause the dialog to show up immediately when calling showIfMeetsConditions() (no conditions will be checked)
.setDebug(isDebug: Boolean) // default is false

Other methods

  • Open the mail feedback directly without showing up the rating dialog
AppRating.openMailFeedback(context: Context, mailSettings: MailSettings)
  • Open your app's Play Store listing without showing up the rating dialog
AppRating.openPlayStoreListing(context: Context)
  • Check if the dialog has been agreed. This is true if the user has clicked the rate now button or if he gave you a rating below the defined threshold.
AppRating.isDialogAgreed(context: Context)
  • Check if the later button has already been clicked
AppRating.wasLaterButtonClicked(context: Context)
  • Check if the never button has already been clicked
AppRating.wasNeverButtonClicked(context: Context)
  • Get the number of later button clicks
AppRating.getNumberOfLaterButtonClicks(context: Context)
  • Reset all library settings to factory default
AppRating.reset(context: Context)

Orientation Change

If the orientation is changed, the onCreate() method will be called again and so does the Builder. These additional calls will distort the library behavior because each call of showIfMeetsConditions() will increase the counted app launches. To guarantee the correct behavior, you have to check for the savedInstanceState like this:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    if (savedInstanceState == null) {
        AppRating.Builder(this)
                // your configuration
                .showIfMeetsConditions()
    }
}

Custom Conditions

You can easily use custom conditions to show the dialog not (only) on app start but e.g. directly after the nth user interaction. Just call the Builder with your conditions and dontCountThisAsAppLaunch():

AppRating.Builder(this)
    // your other settings
    .setCustomCondition { buttonClicks > 10 }
    .setCustomConditionToShowAgain { buttonClicks > 20 }
    .dontCountThisAsAppLaunch()
    .showIfMeetsConditions()

If you want to show the dialog on app start, but with your custom conditions, you can of course just call the Builder in your onCreate() method of your main Activity class. If so, don't forget to remove the dontCountThisAsAppLaunch() method from the example above.

Jetpack Compose

The libraries dialog is implemented as a DialogFragment and thus needs a FragmentActivity to get displayed. If you use Jetpack Compose your activity maybe extends from ComponentActivity and because this class isn't a subtype of FragmentActivity the dialog won't show up. You'll only see an error message in LogCat.

To get it working you just have to change your activity to extend from AppCompatActivity instead (or FragmentActivity). Or you just use the official Google in-app review which doesn't depend on fragments.

Note

  • If the in-app review from Google will be used: After the first in-app review flow was completed successfully the toShowAgain conditions will be used. For example .setMinimumLaunchTimesToShowAgain(launchTimesToShowAgain: Int) instead of .setMinimumLaunchTimes(launchTimes: Int)
  • Use a MaterialComponent theme for better design
  • Don't forget to set up the mail settings if you want to use the mail feedback dialog (otherwise nothing will happen)
  • Use setRatingThreshold(RatingThreshold.NONE) if you don't want to show the feedback form to the user
  • If you set setUseCustomFeedback() to true, you have to handle the feedback text by yourself by adding a click listener (setCustomFeedbackButtonClickListener())
  • If the user rates below the defined minimum threshold, the feedback dialog will be displayed and then the dialog will not show up again
  • If you don't want to customize anything, you can just use AppRating.Builder(this).showIfMeetsConditions() without any settings
  • App launches will only get counted if you call showIfMeetsConditions() and dontCountThisAsAppLaunch() hasn't been called
  • If you have any problems, check out the logs in Logcat (You can filter by awesome_app_rating)
  • Look at the example app to get first impressions

Recommendations

The following things are highly recommended to not annoy the user, which in turn could lead to negative reviews:

  • Don't show the dialog immediately after install
  • Don't set the rating threshold to 5
  • Show the Never button (after n times) so the user can decide whether or not to rate your app
  • Use the methods openPlayStoreListing() and openMailFeedback() in your app settings to give the user the ability of unprompted feedback
  • Don't use AppRating.reset(this) in your production app

License

Copyright (C) 2023 SuddenH4X

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.