diff --git a/AppRater/build.gradle b/AppRater/build.gradle index 4f7b172..ccb2333 100644 --- a/AppRater/build.gradle +++ b/AppRater/build.gradle @@ -6,7 +6,7 @@ android { defaultConfig { minSdkVersion 8 - targetSdkVersion 18 + targetSdkVersion 29 versionName project.VERSION_NAME versionCode Integer.parseInt(project.VERSION_CODE) } diff --git a/AppRater/src/main/AndroidManifest.xml b/AppRater/src/main/AndroidManifest.xml index d968d15..d9e2f86 100644 --- a/AppRater/src/main/AndroidManifest.xml +++ b/AppRater/src/main/AndroidManifest.xml @@ -1,7 +1,5 @@ - + \ No newline at end of file diff --git a/AppRater/src/main/java/org/codechimp/apprater/AppRater.java b/AppRater/src/main/java/org/codechimp/apprater/AppRater.java index 806bd4a..c432a56 100644 --- a/AppRater/src/main/java/org/codechimp/apprater/AppRater.java +++ b/AppRater/src/main/java/org/codechimp/apprater/AppRater.java @@ -11,9 +11,14 @@ import android.content.SharedPreferences; import android.os.Build; import android.util.Log; -import android.widget.Toast; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; public class AppRater { + private final static String TAG = AppRater.class.getSimpleName(); // Preference Constants private final static String PREF_NAME = "apprater"; private final static String PREF_LAUNCH_COUNT = "launch_count"; @@ -33,8 +38,12 @@ public class AppRater { private static boolean isVersionNameCheckEnabled; private static boolean isVersionCodeCheckEnabled; private static boolean isCancelable = true; + private static Callable beforeRateAction; + private static Callable beforePostponeAction; + private static Callable beforeCancelAction; private static Market market = new GoogleMarket(); + private static ExecutorService executor = Executors.newSingleThreadExecutor(); /** * Decides if the version name check is active or not @@ -196,7 +205,7 @@ public static void rateNow(final Context context) { try { context.startActivity(new Intent(Intent.ACTION_VIEW, market.getMarketURI(context))); } catch (ActivityNotFoundException activityNotFoundException1) { - Log.e(AppRater.class.getSimpleName(), "Market Intent not found"); + Log.e(TAG, "Market Intent not found"); } } @@ -257,10 +266,12 @@ private static void showRateAlertDialog(final Context context, final SharedPrefe builder.setPositiveButton(context.getString(R.string.rate), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { - rateNow(context); - if (editor != null) { - editor.putBoolean(PREF_DONT_SHOW_AGAIN, true); - commitOrApply(editor); + if (beforeRateAction == null || execute(beforeRateAction)) { + rateNow(context); + if (editor != null) { + editor.putBoolean(PREF_DONT_SHOW_AGAIN, true); + commitOrApply(editor); + } } dialog.dismiss(); } @@ -269,13 +280,15 @@ public void onClick(DialogInterface dialog, int id) { builder.setNeutralButton(context.getString(R.string.later), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { - if (editor != null) { - Long date_firstLaunch = System.currentTimeMillis(); - editor.putLong(PREF_FIRST_LAUNCHED, date_firstLaunch); - editor.putLong(PREF_LAUNCH_COUNT, 0); - editor.putBoolean(PREF_REMIND_LATER, true); - editor.putBoolean(PREF_DONT_SHOW_AGAIN, false); - commitOrApply(editor); + if (beforePostponeAction == null || execute(beforePostponeAction)) { + if (editor != null) { + Long date_firstLaunch = System.currentTimeMillis(); + editor.putLong(PREF_FIRST_LAUNCHED, date_firstLaunch); + editor.putLong(PREF_LAUNCH_COUNT, 0); + editor.putBoolean(PREF_REMIND_LATER, true); + editor.putBoolean(PREF_DONT_SHOW_AGAIN, false); + commitOrApply(editor); + } } dialog.dismiss(); } @@ -284,13 +297,15 @@ public void onClick(DialogInterface dialog, int id) { builder.setNegativeButton(context.getString(R.string.no_thanks), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { - if (editor != null) { - editor.putBoolean(PREF_DONT_SHOW_AGAIN, true); - editor.putBoolean(PREF_REMIND_LATER, false); - long date_firstLaunch = System.currentTimeMillis(); - editor.putLong(PREF_FIRST_LAUNCHED, date_firstLaunch); - editor.putLong(PREF_LAUNCH_COUNT, 0); - commitOrApply(editor); + if (beforeCancelAction == null || execute(beforeCancelAction)) { + if (editor != null) { + editor.putBoolean(PREF_DONT_SHOW_AGAIN, true); + editor.putBoolean(PREF_REMIND_LATER, false); + long date_firstLaunch = System.currentTimeMillis(); + editor.putLong(PREF_FIRST_LAUNCHED, date_firstLaunch); + editor.putLong(PREF_LAUNCH_COUNT, 0); + commitOrApply(editor); + } } dialog.dismiss(); } @@ -299,6 +314,17 @@ public void onClick(DialogInterface dialog, int id) { builder.show(); } + private static boolean execute(Callable action) { + try { + return executor.submit(action).get(); + } catch (InterruptedException e) { + Log.e(TAG, "error in custom action", e); + } catch (ExecutionException e) { + Log.e(TAG, "error in custom action", e); + } + return false; + } + @SuppressLint("NewApi") private static void commitOrApply(SharedPreferences.Editor editor) { if (Build.VERSION.SDK_INT > 8) { @@ -318,4 +344,31 @@ public static void resetData(Context context) { editor.putLong(PREF_FIRST_LAUNCHED, date_firstLaunch); commitOrApply(editor); } + + /** + * Register a callback to be invoked when 'Rate Now' button is clicked. + * Callback will be executed in background thread, so be sure to not touch UI directly. + * @param action custom action; returns true if market should be opened, false otherwise + */ + public static void onRateClick(Callable action) { + beforeRateAction = action; + } + + /** + * Register a callback to be invoked when 'Later' button is clicked. + * Callback will be executed in background thread, so be sure to not touch UI directly. + * @param action custom action; returns true if user decision should be saved, false otherwise + */ + public static void onLaterClick(Callable action) { + beforePostponeAction = action; + } + + /** + * Register a callback to be invoked when 'No, thanks' button is clicked. + * Callback will be executed in background thread, so be sure to not touch UI directly. + * @param action custom action; returns true if user decision should be saved, false otherwise + */ + public static void onNoClick(Callable action) { + beforeCancelAction = action; + } } diff --git a/AppRaterDemo/build.gradle b/AppRaterDemo/build.gradle index 5282ab1..b66b4d0 100644 --- a/AppRaterDemo/build.gradle +++ b/AppRaterDemo/build.gradle @@ -6,7 +6,7 @@ android { defaultConfig { minSdkVersion 8 - targetSdkVersion 18 + targetSdkVersion 29 versionName project.VERSION_NAME versionCode Integer.parseInt(project.VERSION_CODE) } diff --git a/AppRaterDemo/project.properties b/AppRaterDemo/project.properties index ae90fca..02d7230 100644 --- a/AppRaterDemo/project.properties +++ b/AppRaterDemo/project.properties @@ -12,4 +12,4 @@ # Project target. target=android-18 -android.library.reference.1=../AppRater +android.library.reference.1=../AppRaterLib diff --git a/AppRaterDemo/src/main/java/org/codechimp/appraterdemo/MainActivity.java b/AppRaterDemo/src/main/java/org/codechimp/appraterdemo/MainActivity.java index 800484e..fee2500 100644 --- a/AppRaterDemo/src/main/java/org/codechimp/appraterdemo/MainActivity.java +++ b/AppRaterDemo/src/main/java/org/codechimp/appraterdemo/MainActivity.java @@ -5,6 +5,8 @@ import org.codechimp.apprater.GoogleMarket; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -12,6 +14,9 @@ import android.view.View.OnClickListener; import android.widget.Button; import android.app.Activity; +import android.widget.Toast; + +import java.util.concurrent.Callable; public class MainActivity extends Activity { @@ -33,7 +38,20 @@ public void onClick(View v) { } }); - + // You can intercept button clicks to add custom logic + // Return true if you'd like to process button click as usual or false if processing should be interrupted + AppRater.onRateClick(new Callable() { + @Override + public Boolean call() throws Exception { + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + Toast.makeText(MainActivity.this, "Opening store...", Toast.LENGTH_SHORT).show(); + } + }); + return true; + } + }); // Optionally you can set the Market you want to use prior to calling app_launched // If setMarket not called it will default to Google Play // Current implementations are Google Play and Amazon App Store, you can add your own by implementing Market diff --git a/README.md b/README.md index 9ad280c..fe22f60 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ You can implement your own market, implementing the Market interface and parse y If you want to have a "Rate Now" menu option to go straight to your play store listing call `AppRater.rateNow(this);` within your menu code. +You can intercept button clicks to add custom logic. Use `onRateClick`, `onLaterClick` and `onNoClick` to perform additional actions like analytics reporting. Try out the demo within this repository. ## Gradle diff --git a/build.gradle b/build.gradle index 48a1506..a63b586 100644 --- a/build.gradle +++ b/build.gradle @@ -1,16 +1,21 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { + jcenter() mavenCentral() + maven { + url 'https://maven.google.com/' + name 'Google' + } } dependencies { - classpath 'com.android.tools.build:gradle:1.0.1' + classpath 'com.android.tools.build:gradle:4.0.0' } } ext { - compileSdkVersion = 19 - buildToolsVersion = "19.1.0" + compileSdkVersion = 29 + buildToolsVersion = "30.0.2" } def isReleaseBuild() { diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 0087cd3..fd7e590 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c5ac12a..cb0dc18 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun Dec 07 11:28:32 GMT 2014 +#Mon Oct 05 15:24:08 CEST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip diff --git a/gradlew b/gradlew old mode 100644 new mode 100755