├── Screenshot.png
├── .editorconfig
├── Source
├── AndroidManifest.xml
├── .gitignore
├── project.properties
└── src
│ └── im
│ └── delight
│ └── apprater
│ └── AppRater.java
├── README.md
└── LICENSE
/Screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/delight-im/AppRater/HEAD/Screenshot.png
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = tab
7 | trim_trailing_whitespace = true
8 | end_of_line = lf
9 | insert_final_newline = true
10 |
--------------------------------------------------------------------------------
/Source/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
33 | * Users will be redirected to Google Play by default, but any other app store can be used by providing a custom URI (via setTargetUri(...)) 34 | *
35 | * This library uses English default phrases but you may provide custom phrases either as Strings or resource IDs (via setPhrases(...)) 36 | *
37 | * Users will be prompted to rate your app after at least 2 days and 5 app launches (customizable via setDaysBeforePrompt(...) and setLaunchesBeforePrompt(...)) 38 | *
39 | * You may overwrite the default preference group name and key names by calling setPreferenceKeys(...) (usually not necessary) 40 | *
41 | * The prompting dialog will be displayed (if adequate) as soon as you call show() on your AppRater instance 42 | *
43 | * The dialog will only be shown if at least one application is available on the user's phone to handle the Intent that is defined by the target URI 44 | *
45 | * AlertDialog is used so that the prompt window adapts to your application's styles and themes 46 | *
47 | * Minimum API level: 8 (Android 2.2) 48 | */ 49 | public class AppRater { 50 | 51 | private static final String DEFAULT_PREF_GROUP = "app_rater"; 52 | private static final String DEFAULT_PREFERENCE_DONT_SHOW = "flag_dont_show"; 53 | private static final String DEFAULT_PREFERENCE_LAUNCH_COUNT = "launch_count"; 54 | private static final String DEFAULT_PREFERENCE_FIRST_LAUNCH = "first_launch_time"; 55 | private static final String DEFAULT_TARGET_URI = "market://details?id=%s"; 56 | private static final String DEFAULT_TEXT_TITLE = "Rate this app"; 57 | private static final String DEFAULT_TEXT_EXPLANATION = "Has this app convinced you? We would be very happy if you could rate our application on Google Play. Thanks for your support!"; 58 | private static final String DEFAULT_TEXT_NOW = "Rate now"; 59 | private static final String DEFAULT_TEXT_LATER = "Later"; 60 | private static final String DEFAULT_TEXT_NEVER = "No, thanks"; 61 | private static final int DEFAULT_DAYS_BEFORE_PROMPT = 2; 62 | private static final int DEFAULT_LAUNCHES_BEFORE_PROMPT = 5; 63 | private final Context mContext; 64 | private final String mPackageName; 65 | private int mDaysBeforePrompt; 66 | private int mLaunchesBeforePrompt; 67 | private String mTargetUri; 68 | private String mText_title; 69 | private String mText_explanation; 70 | private String mText_buttonNow; 71 | private String mText_buttonLater; 72 | private String mText_buttonNever; 73 | private String mPrefGroup; 74 | private String mPreference_dontShow; 75 | private String mPreference_launchCount; 76 | private String mPreference_firstLaunch; 77 | private Intent mTargetIntent; 78 | 79 | /** 80 | * Creates a new AppRater instance 81 | * @param context the Activity reference to use for this instance (usually the Activity you called this from) 82 | */ 83 | public AppRater(final Context context) { 84 | this(context, context.getPackageName()); 85 | } 86 | 87 | /** 88 | * Creates a new AppRater instance 89 | * @param context the Activity reference to use for this instance (usually the Activity you called this from) 90 | * @param packageName your application's package name that will be used to open the ratings page 91 | */ 92 | public AppRater(final Context context, final String packageName) { 93 | if (context == null) { 94 | throw new RuntimeException("context may not be null"); 95 | } 96 | mContext = context; 97 | mPackageName = packageName; 98 | mDaysBeforePrompt = DEFAULT_DAYS_BEFORE_PROMPT; 99 | mLaunchesBeforePrompt = DEFAULT_LAUNCHES_BEFORE_PROMPT; 100 | mTargetUri = DEFAULT_TARGET_URI; 101 | mText_title = DEFAULT_TEXT_TITLE; 102 | mText_explanation = DEFAULT_TEXT_EXPLANATION; 103 | mText_buttonNow = DEFAULT_TEXT_NOW; 104 | mText_buttonLater = DEFAULT_TEXT_LATER; 105 | mText_buttonNever = DEFAULT_TEXT_NEVER; 106 | mPrefGroup = DEFAULT_PREF_GROUP; 107 | mPreference_dontShow = DEFAULT_PREFERENCE_DONT_SHOW; 108 | mPreference_launchCount = DEFAULT_PREFERENCE_LAUNCH_COUNT; 109 | mPreference_firstLaunch = DEFAULT_PREFERENCE_FIRST_LAUNCH; 110 | } 111 | 112 | /** 113 | * Sets how many days to wait before users may be prompted 114 | * 115 | * @param days the number of days to wait before showing the prompt 116 | */ 117 | public void setDaysBeforePrompt(final int days) { 118 | mDaysBeforePrompt = days; 119 | } 120 | 121 | /** 122 | * Sets how often users must have launched the app (i.e. called AppRater.show()) before they may be prompted 123 | * 124 | * @param launches the number of launches to wait before showing the prompt 125 | */ 126 | public void setLaunchesBeforePrompt(final int launches) { 127 | mLaunchesBeforePrompt = launches; 128 | } 129 | 130 | /** 131 | * Sets the target URI string that must contain exactly one placeholder (`%s`) for the package name 132 | * 133 | * @param uri the target URI to open when the user wants to rate the app (must include `%s`) 134 | */ 135 | public void setTargetUri(final String uri) { 136 | mTargetUri = uri; 137 | } 138 | 139 | /** 140 | * Sets the given Strings to use for the prompt 141 | * 142 | * @param title the title for the prompt window (as a string) 143 | * @param explanation the explanatory text that will be shown inside the prompt window (as a string) 144 | * @param buttonNow the caption for the `Rate now` button (as a string) 145 | * @param buttonLater the caption for the `Maybe later` button (as a string) 146 | * @param buttonNever the caption for the `Never` button (as a string) 147 | */ 148 | public void setPhrases(final String title, final String explanation, final String buttonNow, final String buttonLater, final String buttonNever) { 149 | mText_title = title; 150 | mText_explanation = explanation; 151 | mText_buttonNow = buttonNow; 152 | mText_buttonLater = buttonLater; 153 | mText_buttonNever = buttonNever; 154 | } 155 | 156 | /** 157 | * Sets the Strings referenced by the given resource IDs to use for the prompt 158 | * 159 | * @param title the title for the prompt window (as a resource ID) 160 | * @param explanation the explanatory text that will be shown inside the prompt window (as a resource ID) 161 | * @param buttonNow the caption for the `Rate now` button (as a resource ID) 162 | * @param buttonLater the caption for the `Maybe later` button (as a resource ID) 163 | * @param buttonNever the caption for the `Never` button (as a resource ID) 164 | */ 165 | public void setPhrases(final int title, final int explanation, final int buttonNow, final int buttonLater, final int buttonNever) { 166 | try { 167 | mText_title = mContext.getString(title); 168 | } 169 | catch (Exception e) { 170 | mText_title = DEFAULT_TEXT_TITLE; 171 | } 172 | try { 173 | mText_explanation = mContext.getString(explanation); 174 | } 175 | catch (Exception e) { 176 | mText_explanation = DEFAULT_TEXT_EXPLANATION; 177 | } 178 | try { 179 | mText_buttonNow = mContext.getString(buttonNow); 180 | } 181 | catch (Exception e) { 182 | mText_buttonNow = DEFAULT_TEXT_NOW; 183 | } 184 | try { 185 | mText_buttonLater = mContext.getString(buttonLater); 186 | } 187 | catch (Exception e) { 188 | mText_buttonLater = DEFAULT_TEXT_LATER; 189 | } 190 | try { 191 | mText_buttonNever = mContext.getString(buttonNever); 192 | } 193 | catch (Exception e) { 194 | mText_buttonNever = DEFAULT_TEXT_NEVER; 195 | } 196 | } 197 | 198 | /** 199 | * Sets the given keys for the preferences that will be used 200 | * @param group the preference group file that all values of this class go in 201 | * @param dontShow the preference name for the dont-show flag 202 | * @param launchCount the preference name for the launch counter 203 | * @param firstLaunchTime the preference name for the first launch time value 204 | */ 205 | public void setPreferenceKeys(String group, String dontShow, String launchCount, String firstLaunchTime) { 206 | mPrefGroup = group; 207 | mPreference_dontShow = dontShow; 208 | mPreference_launchCount = launchCount; 209 | mPreference_firstLaunch = firstLaunchTime; 210 | } 211 | 212 | private void createTargetIntent() { 213 | mTargetIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(String.format(mTargetUri, mPackageName))); 214 | } 215 | 216 | /** 217 | * Checks if the rating dialog should be shown to the user and displays it if needed 218 | * 219 | * @return the AlertDialog that has been shown or null 220 | */ 221 | @SuppressLint("CommitPrefEdits") 222 | public AlertDialog show() { 223 | createTargetIntent(); 224 | 225 | final SharedPreferences prefs = mContext.getSharedPreferences(mPrefGroup, 0); 226 | final SharedPreferences.Editor editor = prefs.edit(); 227 | 228 | if (prefs.getBoolean(mPreference_dontShow, false)) { // if user opted not to rate the app 229 | return null; // do not show anything 230 | } 231 | 232 | long launch_count = prefs.getLong(mPreference_launchCount, 0); // get launch counter 233 | launch_count++; // increase number of launches by one 234 | editor.putLong(mPreference_launchCount, launch_count); // write new counter back to preference 235 | 236 | long firstLaunchTime = prefs.getLong(mPreference_firstLaunch, 0); // get date of first launch 237 | if (firstLaunchTime == 0) { // if not set yet 238 | firstLaunchTime = System.currentTimeMillis(); // set to current time 239 | editor.putLong(mPreference_firstLaunch, firstLaunchTime); 240 | } 241 | 242 | savePreferences(editor); 243 | 244 | if (launch_count >= mLaunchesBeforePrompt) { // wait at least x app launches 245 | if (System.currentTimeMillis() >= (firstLaunchTime + (mDaysBeforePrompt * DateUtils.DAY_IN_MILLIS))) { // wait at least x days 246 | try { 247 | return showDialog(mContext, editor); 248 | } 249 | catch (Exception e) { 250 | return null; 251 | } 252 | } 253 | else { 254 | return null; 255 | } 256 | } 257 | else { 258 | return null; 259 | } 260 | } 261 | 262 | /** 263 | * Displays a preview of the prompt as it will be shown to users later (use as a replacement for `show()`) 264 | * 265 | * @return the AlertDialog that has been shown or null 266 | * @deprecated you should remove any reference to this method before deploying a production version 267 | */ 268 | @Deprecated 269 | public AlertDialog demo() { 270 | createTargetIntent(); 271 | return showDialog(mContext, mContext.getSharedPreferences(mPrefGroup, 0).edit()); 272 | } 273 | 274 | @SuppressLint("NewApi") 275 | private static void savePreferences(SharedPreferences.Editor editor) { 276 | if (editor != null) { 277 | if (Build.VERSION.SDK_INT < 9) { 278 | editor.commit(); 279 | } 280 | else { 281 | editor.apply(); 282 | } 283 | } 284 | } 285 | 286 | private static void closeDialog(DialogInterface dialog) { 287 | if (dialog != null) { 288 | dialog.dismiss(); 289 | } 290 | } 291 | 292 | private void setDontShow(SharedPreferences.Editor editor) { 293 | if (editor != null) { 294 | editor.putBoolean(mPreference_dontShow, true); 295 | savePreferences(editor); 296 | } 297 | } 298 | 299 | private void setFirstLaunchTime(SharedPreferences.Editor editor, long time) { 300 | if (editor != null) { 301 | editor.putLong(mPreference_firstLaunch, time); 302 | savePreferences(editor); 303 | } 304 | } 305 | 306 | private void buttonNowClick(SharedPreferences.Editor editor, DialogInterface dialog, Context context) { 307 | setDontShow(editor); 308 | closeDialog(dialog); 309 | try { 310 | context.startActivity(mTargetIntent); 311 | } 312 | catch (ActivityNotFoundException ignored) {} 313 | } 314 | 315 | private void buttonLaterClick(SharedPreferences.Editor editor, DialogInterface dialog) { 316 | setFirstLaunchTime(editor, System.currentTimeMillis()); 317 | closeDialog(dialog); 318 | } 319 | 320 | private void buttonNeverClick(SharedPreferences.Editor editor, DialogInterface dialog) { 321 | setDontShow(editor); 322 | closeDialog(dialog); 323 | } 324 | 325 | private AlertDialog showDialog(final Context context, final SharedPreferences.Editor editor) { 326 | final AlertDialog.Builder rateDialog = new AlertDialog.Builder(context); 327 | rateDialog.setTitle(mText_title); 328 | rateDialog.setMessage(mText_explanation); 329 | rateDialog.setNeutralButton(mText_buttonLater, new DialogInterface.OnClickListener() { 330 | @Override 331 | public void onClick(DialogInterface dialog, int which) { 332 | buttonLaterClick(editor, dialog); 333 | } 334 | }); 335 | rateDialog.setPositiveButton(mText_buttonNow, new DialogInterface.OnClickListener() { 336 | @Override 337 | public void onClick(DialogInterface dialog, int which) { 338 | buttonNowClick(editor, dialog, context); 339 | } 340 | }); 341 | rateDialog.setNegativeButton(mText_buttonNever, new DialogInterface.OnClickListener() { 342 | @Override 343 | public void onClick(DialogInterface dialog, int which) { 344 | buttonNeverClick(editor, dialog); 345 | } 346 | }); 347 | return rateDialog.show(); 348 | } 349 | 350 | } 351 | --------------------------------------------------------------------------------