Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.vorlonsoft.android.rate.AppRate Maven / Gradle / Ivy
Go to download
Library to help you promote your Android app by prompting users to rate the app after using it for a few days.
/*
* Copyright 2017 - 2018 Vorlonsoft LLC
*
* Licensed under The MIT License (MIT)
*/
package com.vorlonsoft.android.rate;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.ArrayMap;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import java.lang.ref.WeakReference;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import androidx.annotation.NonNull;
import static com.vorlonsoft.android.rate.Constants.Date.DAY_IN_MILLIS;
import static com.vorlonsoft.android.rate.Constants.Utils.TAG;
import static com.vorlonsoft.android.rate.PreferenceHelper.get365DayPeriodDialogLaunchTimes;
import static com.vorlonsoft.android.rate.PreferenceHelper.getCustomEventCount;
import static com.vorlonsoft.android.rate.PreferenceHelper.getInstallDate;
import static com.vorlonsoft.android.rate.PreferenceHelper.getIsAgreeShowDialog;
import static com.vorlonsoft.android.rate.PreferenceHelper.getLaunchTimes;
import static com.vorlonsoft.android.rate.PreferenceHelper.getRemindInterval;
import static com.vorlonsoft.android.rate.PreferenceHelper.isFirstLaunch;
import static com.vorlonsoft.android.rate.PreferenceHelper.setCustomEventCount;
import static com.vorlonsoft.android.rate.PreferenceHelper.setFirstLaunchSharedPreferences;
import static com.vorlonsoft.android.rate.PreferenceHelper.setIsAgreeShowDialog;
import static com.vorlonsoft.android.rate.StoreType.AMAZON;
import static com.vorlonsoft.android.rate.StoreType.APPLE;
import static com.vorlonsoft.android.rate.StoreType.BLACKBERRY;
import static com.vorlonsoft.android.rate.StoreType.INTENT;
import static com.vorlonsoft.android.rate.StoreType.OTHER;
import static com.vorlonsoft.android.rate.StoreType.YANDEX;
/**
* AndroidRate is a library to help you promote your Android app
* by prompting users to rate the app after using it for a few days.
* AppRate Class - main class of the AndroidRate library, thread-safe
* and a fast singleton implementation.
*
* @author Alexander Savin
* @author Shintaro Katafuchi
* @version 1.1.9
* @since 0.0.4
*/
public final class AppRate {
@SuppressLint("StaticFieldLeak")
private static volatile AppRate singleton = null;
private final Map customEventsCounts;
private final Context context;
private final DialogOptions dialogOptions = new DialogOptions();
private final StoreOptions storeOptions = new StoreOptions();
private boolean isDebug = false;
private byte installDate = (byte) 10;
private byte appLaunchTimes = (byte) 10;
private byte remindInterval = (byte) 1;
private byte remindLaunchTimes = (byte) 1;
/**
* Short.MAX_VALUE - unlimited occurrences of the display of the dialog within a 365-day period
*/
private short dialogLaunchTimes = Short.MAX_VALUE;
// Weak ref to avoid leaking the context
private WeakReference dialog = null;
private DialogManager.Factory dialogManagerFactory = new DefaultDialogManager.Factory();
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
customEventsCounts = new ArrayMap<>();
} else {
customEventsCounts = new HashMap<>();
}
}
private AppRate(Context context) {
this.context = context.getApplicationContext();
}
public static AppRate with(Context context) {
if (singleton == null) {
synchronized (AppRate.class) {
if (singleton == null) {
singleton = new AppRate(context);
}
}
}
return singleton;
}
/**
* Shows rate dialog when meets conditions.
* Call this method at the end of your onCreate() method to determine whether
* to show the rate dialog or not. It will check if the conditions are met and
* show rate dialog if yes.
*
* @param activity your activity, use "this" in most cases
*/
@SuppressWarnings("UnusedReturnValue")
public static boolean showRateDialogIfMeetsConditions(Activity activity) {
boolean isMeetsConditions = ((singleton != null) && (singleton.isDebug() || singleton.shouldShowRateDialog()));
if (isMeetsConditions) {
singleton.showRateDialog(activity);
}
return isMeetsConditions;
}
private static boolean isOverDate(long targetDate, byte threshold) {
return new Date().getTime() - targetDate >= threshold * DAY_IN_MILLIS;
}
private boolean isBelow365DayPeriodMaxNumberDialogLaunchTimes() {
return ((dialogLaunchTimes == Short.MAX_VALUE) || (get365DayPeriodDialogLaunchTimes(context) < dialogLaunchTimes));
}
/**
* Clear dialog weak reference
*/
void clearDialog() {
if (dialog != null) {
dialog.clear();
}
}
/**
* Sets max number occurrences of the display of the dialog within a 365-day period.
* Set Short.MAX_VALUE for unlimited occurrences.
*
* @param dialogLaunchTimes max number of launches within a 365-day period, default is unlimited
*/
@SuppressWarnings({"unused"})
public AppRate set365DayPeriodMaxNumberDialogLaunchTimes(short dialogLaunchTimes) {
this.dialogLaunchTimes = dialogLaunchTimes;
return this;
}
/**
* Sets the number of launches until the rating dialog pops up for the first time
*
* @param appLaunchTimes number of launches, default is 10
*/
public AppRate setLaunchTimes(@SuppressWarnings("SameParameterValue") byte appLaunchTimes) {
this.appLaunchTimes = appLaunchTimes;
return this;
}
/**
* Sets the number of days until the rating dialog pops up for the first time
*
* @param installDate number of days, default is 10
*/
public AppRate setInstallDays(@SuppressWarnings("SameParameterValue") byte installDate) {
this.installDate = installDate;
return this;
}
/**
* Sets number of days until rating dialog pops up for next time after
* neutral button clicked
*
* @param remindInterval number of days, default is 1
*/
public AppRate setRemindInterval(@SuppressWarnings("SameParameterValue") byte remindInterval) {
this.remindInterval = remindInterval;
return this;
}
@SuppressWarnings("unused")
public AppRate setMinimumEventCount(String eventName, short minimumCount) {
this.customEventsCounts.put(eventName, minimumCount);
return this;
}
public AppRate setRemindLaunchTimes(@SuppressWarnings("SameParameterValue") byte remindLaunchTimes) {
this.remindLaunchTimes = remindLaunchTimes;
return this;
}
/**
* Decides if Neutral button appear in the rating dialog or not
*
* @param isShowNeutralButton default is true
*/
public AppRate setShowLaterButton(@SuppressWarnings("SameParameterValue") boolean isShowNeutralButton) {
dialogOptions.setShowNeutralButton(isShowNeutralButton);
return this;
}
/**
* Decides if Never button appear in the rating dialog or not
*
* @param isShowNeverButton default is true
*/
@SuppressWarnings("unused")
public AppRate setShowNeverButton(boolean isShowNeverButton) {
dialogOptions.setShowNegativeButton(isShowNeverButton);
return this;
}
@SuppressWarnings("unused")
public AppRate setShowTitle(boolean isShowTitle) {
dialogOptions.setShowTitle(isShowTitle);
return this;
}
@SuppressWarnings("unused")
public AppRate clearAgreeShowDialog() {
setIsAgreeShowDialog(context, true);
return this;
}
@SuppressWarnings("unused")
public AppRate clearSettingsParam() {
PreferenceHelper.clearSharedPreferences(context);
return this;
}
@SuppressWarnings("unused")
public AppRate setAgreeShowDialog(boolean isAgree) {
setIsAgreeShowDialog(context, isAgree);
return this;
}
@SuppressWarnings("unused")
public AppRate setView(View view) {
dialogOptions.setView(view);
return this;
}
public AppRate setOnClickButtonListener(OnClickButtonListener listener) {
dialogOptions.setListener(listener);
return this;
}
@SuppressWarnings("unused")
public AppRate setTitle(@SuppressWarnings("SameParameterValue") int resourceId) {
dialogOptions.setTitleResId(resourceId);
return this;
}
@SuppressWarnings("unused")
public AppRate setTitle(String title) {
dialogOptions.setTitleText(title);
return this;
}
@SuppressWarnings("unused")
public AppRate setMessage(int resourceId) {
dialogOptions.setMessageResId(resourceId);
return this;
}
@SuppressWarnings("unused")
public AppRate setMessage(String message) {
dialogOptions.setMessageText(message);
return this;
}
@SuppressWarnings("unused")
public AppRate setTextRateNow(@SuppressWarnings("SameParameterValue") int resourceId) {
dialogOptions.setTextPositiveResId(resourceId);
return this;
}
@SuppressWarnings("unused")
public AppRate setTextRateNow(String positiveText) {
dialogOptions.setPositiveText(positiveText);
return this;
}
@SuppressWarnings("unused")
public AppRate setTextLater(@SuppressWarnings("SameParameterValue") int resourceId) {
dialogOptions.setTextNeutralResId(resourceId);
return this;
}
@SuppressWarnings("unused")
public AppRate setTextLater(String neutralText) {
dialogOptions.setNeutralText(neutralText);
return this;
}
@SuppressWarnings("unused")
public AppRate setTextNever(@SuppressWarnings("SameParameterValue") int resourceId) {
dialogOptions.setTextNegativeResId(resourceId);
return this;
}
@SuppressWarnings("unused")
public AppRate setTextNever(String negativeText) {
dialogOptions.setNegativeText(negativeText);
return this;
}
/**
* Sets whether the rating dialog is cancelable or not.
*
* @param cancelable default is false
*/
public AppRate setCancelable(@SuppressWarnings("SameParameterValue") boolean cancelable) {
dialogOptions.setCancelable(cancelable);
return this;
}
public AppRate setStoreType(@StoreType.StoreWithoutApplicationId final int storeType) {
if ((storeType == APPLE) || (storeType == BLACKBERRY)) {
throw new IllegalArgumentException("For StoreType.APPLE/StoreType.BLACKBERRY you must use setStoreType(StoreType.APPLE/StoreType.BLACKBERRY, long applicationId)");
} else if ((storeType < AMAZON) || (storeType > YANDEX)) {
throw new IllegalArgumentException("StoreType must be one of: AMAZON, APPLE, BAZAAR, BLACKBERRY, CHINESESTORES, GOOGLEPLAY, MI, SAMSUNG, SLIDEME, TENCENT, YANDEX");
}
return setStoreType(storeType, null, null);
}
@SuppressWarnings("unused")
public AppRate setStoreType(@StoreType.StoreWithApplicationId final int storeType, final long applicationId) {
if ((storeType < AMAZON) || (storeType > YANDEX)) {
throw new IllegalArgumentException("StoreType must be one of: AMAZON, APPLE, BAZAAR, BLACKBERRY, CHINESESTORES, GOOGLEPLAY, MI, SAMSUNG, SLIDEME, TENCENT, YANDEX");
}
return ((storeType != APPLE) && (storeType != BLACKBERRY)) ? setStoreType(storeType, null, null) : setStoreType(storeType, new String[]{String.valueOf(applicationId)}, null);
}
@SuppressWarnings({"ConstantConditions", "WeakerAccess", "unused"})
public AppRate setStoreType(@NonNull final String... uris) {
if (uris == null) {
throw new IllegalArgumentException("setStoreType(String... uris): 'uris' must be != null");
}
return setStoreType(OTHER, uris, null);
}
private AppRate setStoreType(final int storeType, final String[] stringParam, final Intent[] intentParaam) {
storeOptions.setStoreType(storeType, stringParam, intentParaam);
return this;
}
/**
* Gets the currently set Store
*/
@StoreType.AnyStoreType
public int getStoreType() {
return storeOptions.getStoreType();
}
@SuppressWarnings({"ConstantConditions", "WeakerAccess", "unused"})
public AppRate setStoreType(@NonNull final Intent... intents) {
if (intents == null) {
throw new IllegalArgumentException("setStoreType(Intent... intents): 'intents' must be != null");
}
return setStoreType(INTENT, null, intents);
}
@SuppressWarnings("unused")
public AppRate incrementEventCount(String eventName) {
return setEventCountValue(eventName, (short) (getCustomEventCount(context, eventName) + 1));
}
@SuppressWarnings("WeakerAccess")
public AppRate setEventCountValue(String eventName, short countValue) {
setCustomEventCount(context, eventName, countValue);
return this;
}
/**
* Sets dialog theme. You can use a specific theme to inflate the dialog.
*
* @param themeResId theme resource ID, default is 0
*/
@SuppressWarnings("unused")
public AppRate setThemeResId(int themeResId) {
dialogOptions.setThemeResId(themeResId);
return this;
}
@SuppressWarnings("unused")
public AppRate setDialogManagerFactory(DialogManager.Factory dialogManagerFactory) {
this.dialogManagerFactory = dialogManagerFactory;
return this;
}
/**
* Monitor launch times.
* Call this method when the launcher activity's onCreate() is launched.
*/
public void monitor() {
if (isFirstLaunch(context)) {
setFirstLaunchSharedPreferences(context);
} else {
PreferenceHelper.setLaunchTimes(context, (short) (getLaunchTimes(context) + 1));
}
}
/**
* Call this method directly if you want to force the rate dialog, useful for testing purposes
*
* @param activity your activity, use "this" in most cases
*/
@SuppressWarnings("WeakerAccess")
public void showRateDialog(Activity activity) {
dismissRateDialog();
if (!activity.isFinishing()) {
dialog = new WeakReference<>(dialogManagerFactory.createDialogManager(activity, dialogOptions, storeOptions).createDialog());
if (dialog.get() != null) {
dialog.get().show();
} else {
Log.w(TAG, "Failed to rate app, can't create rate dialog");
}
} else {
Log.w(TAG, "Failed to rate app, can't create rate dialog, because activity is in the process of finishing");
}
}
@SuppressWarnings("WeakerAccess")
public void dismissRateDialog() {
if ((dialog != null) && (dialog.get() != null)) {
dialog.get().dismiss();
clearDialog();
}
}
/**
* Call this method directly to go straight to store listing for rating
*
* @param activity your activity, use "this" in most cases
*/
@SuppressWarnings("unused")
public void rateNow(Activity activity) {
if ((dialog != null) && (dialog.get() != null)) {
Button positiveButton = ((AlertDialog) dialog.get()).getButton(AlertDialog.BUTTON_POSITIVE);
if (positiveButton != null) {
positiveButton.performClick();
}
} else {
dialog = new WeakReference<>(dialogManagerFactory.createDialogManager(activity, dialogOptions, storeOptions).createDialog());
if (dialog.get() != null) {
Button positiveButton = ((AlertDialog) dialog.get()).getButton(AlertDialog.BUTTON_POSITIVE);
if (positiveButton != null) {
positiveButton.performClick();
}
clearDialog();
} else {
Log.w(TAG, "Failed to rate app, can't create rate dialog");
}
}
}
/**
* Call this method to determine whether conditions to show the rate dialog meets or not.
*/
@SuppressWarnings("WeakerAccess")
public boolean shouldShowRateDialog() {
return getIsAgreeShowDialog(context) &&
isOverLaunchTimes() &&
isOverRemindLaunchTimes() &&
isOverInstallDate() &&
isOverRemindDate() &&
isOverCustomEventsRequirements() &&
isBelow365DayPeriodMaxNumberDialogLaunchTimes();
}
private boolean isOverLaunchTimes() {
return ((appLaunchTimes == 0) || (getLaunchTimes(context) >= appLaunchTimes));
}
private boolean isOverRemindLaunchTimes() {
return ((remindLaunchTimes == 1) || ((remindLaunchTimes != 0) && ((getLaunchTimes(context) % remindLaunchTimes) == 0)));
}
private boolean isOverInstallDate() {
return ((installDate == 0) || isOverDate(getInstallDate(context), installDate));
}
private boolean isOverRemindDate() {
return ((remindInterval == 0) || isOverDate(getRemindInterval(context), remindInterval));
}
private boolean isOverCustomEventsRequirements() {
if (customEventsCounts.isEmpty()) {
return true;
} else {
Short currentCount;
for (Map.Entry eventRequirement : customEventsCounts.entrySet()) {
currentCount = getCustomEventCount(context, eventRequirement.getKey());
if (currentCount < eventRequirement.getValue()) {
return false;
}
}
return true;
}
}
@SuppressWarnings({"unused", "WeakerAccess"})
public boolean isDebug() {
return isDebug;
}
public AppRate setDebug(@SuppressWarnings("SameParameterValue") boolean isDebug) {
this.isDebug = isDebug;
return this;
}
}