SuddenH4X / awesome-app-rating

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.
Apache License 2.0
231 stars 40 forks source link
android android-library androidx custom-feedback dialog feedback feedback-dialog feedback-form in-app-review kotlin kotlin-android kotlin-library materialcomponents night-mode nightmode rate rating rating-dialog user-rates user-rating

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

This library:

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:

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

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

.setMinimumDays(minimumDays: Int) // default is 3
.setMinimumLaunchTimes(launchTimes: Int) // default is 5
.setMinimumDaysToShowAgain(minimumDaysToShowAgain: Int) // default is 14
.setMinimumLaunchTimesToShowAgain(launchTimesToShowAgain: Int) // default is 5
.setCustomCondition(customCondition: () -> Boolean)
.setCustomConditionToShowAgain(customConditionToShowAgain: () -> Boolean)
.dontCountThisAsAppLaunch()

Design

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

General
.setIconDrawable(iconDrawable: Drawable?) // default is null which means app icon
.setCustomTheme(customTheme: Int)
.setRateLaterButtonTextId(rateLaterButtonTextId: Int)
.setRateLaterButtonClickListener(rateLaterButtonClickListener: RateDialogClickListener)
.showRateNeverButton(rateNeverButtonTextId: Int, rateNeverButtonClickListener: RateDialogClickListener) // by default the button is hidden
.showRateNeverButtonAfterNTimes(rateNeverButtonTextId: Int, rateNeverButtonClickListener: RateDialogClickListener, countOfLaterButtonClicks: Int)
Rating Overview
.setTitleTextId(titleTextId: Int)
.setMessageTextId(messageTextId: Int) // by default no message is shown
.setConfirmButtonTextId(confirmButtonTextId: Int)
.setConfirmButtonClickListener(confirmButtonClickListener: ConfirmButtonClickListener)
.setShowOnlyFullStars(showOnlyFullStars: Boolean)  // default is false
Store Rating
.setStoreRatingTitleTextId(storeRatingTitleTextId: Int)
.setStoreRatingMessageTextId(storeRatingMessageTextId: Int)
.setRateNowButtonTextId(rateNowButtonTextId: Int)
.overwriteRateNowButtonClickListener(rateNowButtonClickListener: RateDialogClickListener) // by default it opens the Play Store listing of your app
.setAdditionalRateNowButtonClickListener(additionalRateNowButtonClickListener: RateDialogClickListener)
Feedback
.setFeedbackTitleTextId(feedbackTitleTextId: Int)
.setNoFeedbackButtonTextId(noFeedbackButtonTextId: Int)
.setNoFeedbackButtonClickListener(noFeedbackButtonClickListener: RateDialogClickListener)
.setUseCustomFeedback(useCustomFeedback: Boolean) // default is false
Mail Feedback

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

.setMailFeedbackMessageTextId(feedbackMailMessageTextId: Int)
.setMailSettingsForFeedbackDialog(mailSettings: MailSettings)
.setMailFeedbackButtonTextId(mailFeedbackButtonTextId: Int)
.overwriteMailFeedbackButtonClickListener(mailFeedbackButtonClickListener: RateDialogClickListener)
.setAdditionalMailFeedbackButtonClickListener(additionalMailFeedbackButtonClickListener: RateDialogClickListener)
Custom Feedback

These settings will only apply if custom feedback is enabled:

.setCustomFeedbackMessageTextId(feedbackCustomMessageTextId: Int)
.setCustomFeedbackButtonTextId(customFeedbackButtonTextId: Int)
.setCustomFeedbackButtonClickListener(customFeedbackButtonClickListener: CustomFeedbackButtonClickListener)

Other settings

.setRatingThreshold(ratingThreshold: RatingThreshold) // default is RatingThreshold.THREE
.setCancelable(cancelable: Boolean) // default is false
.setDialogCancelListener(dialogCancelListener: () -> Unit)
.setLoggingEnabled(isLoggingEnabled: Boolean) // default is true
.setDebug(isDebug: Boolean) // default is false

Other methods

AppRating.openMailFeedback(context: Context, mailSettings: MailSettings)
AppRating.openPlayStoreListing(context: Context)
AppRating.isDialogAgreed(context: Context)
AppRating.wasLaterButtonClicked(context: Context)
AppRating.wasNeverButtonClicked(context: Context)
AppRating.getNumberOfLaterButtonClicks(context: Context)
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

Recommendations

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

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.