Add in-app reviews for play flavor
Signed-off-by: William Brawner <me@wbrawner.com>
This commit is contained in:
parent
76c45bb914
commit
c6728f1afa
4 changed files with 140 additions and 0 deletions
|
@ -125,6 +125,7 @@ dependencies {
|
|||
implementation 'com.commonsware.cwac:anddown:0.3.0'
|
||||
playImplementation 'com.android.billingclient:billing:3.0.0'
|
||||
playImplementation 'com.google.firebase:firebase-core:17.5.0'
|
||||
playImplementation 'com.google.android.play:core-ktx:1.8.1'
|
||||
implementation "androidx.core:core-ktx:1.3.1"
|
||||
implementation 'androidx.browser:browser:1.2.0'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package com.wbrawner.simplemarkdown.utility
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.play.core.review.ReviewManagerFactory
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
object ReviewHelper {
|
||||
// No review library for F-droid, so this is a no-op
|
||||
fun init(application: Application, errorHandler: ErrorHandler) {
|
||||
Log.w("ReviewHelper", "ReviewHelper not enabled for free builds")
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ import android.content.Context
|
|||
import android.os.StrictMode
|
||||
import com.wbrawner.simplemarkdown.utility.AcraErrorHandler
|
||||
import com.wbrawner.simplemarkdown.utility.ErrorHandler
|
||||
import com.wbrawner.simplemarkdown.utility.ReviewHelper
|
||||
|
||||
class MarkdownApplication : Application() {
|
||||
val errorHandler: ErrorHandler by lazy {
|
||||
|
@ -23,6 +24,7 @@ class MarkdownApplication : Application() {
|
|||
.build())
|
||||
}
|
||||
super.onCreate()
|
||||
ReviewHelper.init(this, errorHandler)
|
||||
}
|
||||
|
||||
override fun attachBaseContext(base: Context?) {
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
package com.wbrawner.simplemarkdown.utility
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.os.SystemClock
|
||||
import android.util.Log
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.play.core.review.ReviewManager
|
||||
import com.google.android.play.core.review.ReviewManagerFactory
|
||||
import com.wbrawner.simplemarkdown.view.activity.MainActivity
|
||||
|
||||
private const val KEY_TIME_IN_APP = "timeInApp"
|
||||
|
||||
// Prompt user to review after they've used the app for 30 minutes
|
||||
private const val TIME_TO_PROMPT = 1000L * 60 * 30
|
||||
|
||||
/**
|
||||
* A simple attempt at a non-intrusive way of prompting long-time users of the app to leave a
|
||||
* review. It works by registering itself as one of the [Application.ActivityLifecycleCallbacks] to
|
||||
* keep track of the current activity, which is needed to launch the review flow, along with the
|
||||
* time the activity is started to estimate how long the user has been active.
|
||||
*/
|
||||
object ReviewHelper : Application.ActivityLifecycleCallbacks {
|
||||
private lateinit var application: Application
|
||||
private lateinit var reviewManager: ReviewManager
|
||||
private lateinit var sharedPreferences: SharedPreferences
|
||||
private lateinit var errorHandler: ErrorHandler
|
||||
private var currentActivity: Activity? = null
|
||||
private var activityCount = 0
|
||||
set(value) {
|
||||
field = if (value < 0) {
|
||||
0
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
private var activeTime = 0L
|
||||
|
||||
fun init(
|
||||
application: Application,
|
||||
errorHandler: ErrorHandler,
|
||||
sharedPreferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(application),
|
||||
reviewManager: ReviewManager = ReviewManagerFactory.create(application)
|
||||
) {
|
||||
this.application = application
|
||||
this.errorHandler = errorHandler
|
||||
this.sharedPreferences = sharedPreferences
|
||||
this.reviewManager = reviewManager
|
||||
if (sharedPreferences.getLong(KEY_TIME_IN_APP, 0L) == -1L) {
|
||||
// We've already prompted the user for the review so let's not be annoying about it
|
||||
Log.i("ReviewHelper", "User already prompted for review, not configuring ReviewHelper")
|
||||
return
|
||||
}
|
||||
application.registerActivityLifecycleCallbacks(this)
|
||||
}
|
||||
|
||||
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
|
||||
// No op
|
||||
}
|
||||
|
||||
override fun onActivityStarted(activity: Activity) {
|
||||
currentActivity = activity
|
||||
if (activityCount++ == 0) {
|
||||
activeTime = SystemClock.elapsedRealtime()
|
||||
}
|
||||
if (activity !is MainActivity || sharedPreferences.getLong(KEY_TIME_IN_APP, 0L) < TIME_TO_PROMPT) {
|
||||
// Not ready to prompt just yet
|
||||
Log.v("ReviewHelper", "Not ready to prompt user for review yet")
|
||||
return
|
||||
}
|
||||
Log.v("ReviewHelper", "Prompting user for review")
|
||||
reviewManager.requestReviewFlow().addOnCompleteListener { request ->
|
||||
if (!request.isSuccessful) {
|
||||
val exception = request.exception
|
||||
?: RuntimeException("Failed to request review")
|
||||
Log.e("ReviewHelper", "Failed to prompt user for review", exception)
|
||||
errorHandler.reportException(exception)
|
||||
return@addOnCompleteListener
|
||||
}
|
||||
|
||||
reviewManager.launchReviewFlow(activity, request.result).addOnCompleteListener { _ ->
|
||||
// According to the docs, this may or may not have actually been shown. Either
|
||||
// way, it's not a critical piece of functionality for the app so I'm not
|
||||
// worried about it failing silently. Link for reference:
|
||||
// https://developer.android.com/guide/playcore/in-app-review/kotlin-java#launch-review-flow
|
||||
Log.v("ReviewHelper", "User finished review, ending activity watch")
|
||||
application.unregisterActivityLifecycleCallbacks(this)
|
||||
sharedPreferences.edit {
|
||||
putLong(KEY_TIME_IN_APP, -1L)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResumed(activity: Activity) {
|
||||
// No op
|
||||
}
|
||||
|
||||
override fun onActivityPaused(activity: Activity) {
|
||||
// No op
|
||||
}
|
||||
|
||||
override fun onActivityStopped(activity: Activity) {
|
||||
currentActivity = null
|
||||
if (--activityCount == 0) {
|
||||
sharedPreferences.edit {
|
||||
putLong(KEY_TIME_IN_APP, SystemClock.elapsedRealtime() - activeTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
|
||||
// No op
|
||||
}
|
||||
|
||||
override fun onActivityDestroyed(activity: Activity) {
|
||||
// No op
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue