Use In-App Review API

pull/1817/head
Alex Baker 4 years ago
parent ce191d3325
commit 2aee91a04b

@ -237,6 +237,8 @@ dependencies {
googleplayImplementation("com.google.android.gms:play-services-location:19.0.1")
googleplayImplementation("com.google.android.gms:play-services-maps:18.0.2")
googleplayImplementation("com.android.billingclient:billing-ktx:3.0.3")
googleplayImplementation("com.google.android.play:core:1.10.3")
googleplayImplementation("com.google.android.play:core-ktx:1.8.1")
androidTestImplementation("com.google.dagger:hilt-android-testing:${Versions.hilt}")
kaptAndroidTest("com.google.dagger:hilt-compiler:${Versions.hilt}")

@ -0,0 +1,8 @@
package org.tasks.play
import android.app.Activity
import javax.inject.Inject
class PlayServices @Inject constructor() {
fun requestReview(@Suppress("UNUSED_PARAMETER") activity: Activity) {}
}

@ -1,12 +0,0 @@
package org.tasks.gtasks
import android.content.Context
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability.getInstance
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
object PlayServices {
fun isAvailable(context: Context) =
getInstance().isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS
}

@ -1,31 +1,36 @@
package org.tasks.injection
import android.content.Context
import dagger.Lazy
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import org.tasks.gtasks.PlayServices
import org.tasks.location.*
import org.tasks.location.Geocoder
import org.tasks.location.GeocoderMapbox
import org.tasks.location.GoogleMapFragment
import org.tasks.location.LocationService
import org.tasks.location.LocationServiceAndroid
import org.tasks.location.LocationServiceGooglePlay
import org.tasks.location.MapFragment
import org.tasks.location.OsmMapFragment
import org.tasks.play.PlayServices
@Module
@InstallIn(SingletonComponent::class)
class FlavorModule {
@Provides
fun getLocationService(
@ApplicationContext context: Context,
google: Lazy<LocationServiceGooglePlay>,
android: Lazy<LocationServiceAndroid>
): LocationService = if (PlayServices.isAvailable(context)) google.get() else android.get()
google: Lazy<LocationServiceGooglePlay>,
android: Lazy<LocationServiceAndroid>,
playServices: PlayServices,
): LocationService = if (playServices.isAvailable()) google.get() else android.get()
@Provides
fun getMapFragment(
@ApplicationContext context: Context,
osm: Lazy<OsmMapFragment>,
google: Lazy<GoogleMapFragment>,
): MapFragment = if (PlayServices.isAvailable(context)) google.get() else osm.get()
osm: Lazy<OsmMapFragment>,
google: Lazy<GoogleMapFragment>,
playServices: PlayServices,
): MapFragment = if (playServices.isAvailable()) google.get() else osm.get()
@Provides
fun getGeocoder(mapbox: GeocoderMapbox): Geocoder = mapbox

@ -0,0 +1,48 @@
package org.tasks.play
import android.app.Activity
import android.content.Context
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability.getInstance
import com.google.android.play.core.ktx.launchReview
import com.google.android.play.core.ktx.requestReview
import com.google.android.play.core.review.ReviewManagerFactory
import com.todoroo.andlib.utility.DateUtilities.now
import dagger.hilt.android.qualifiers.ApplicationContext
import org.tasks.preferences.Preferences
import org.tasks.time.DateTimeUtils.printTimestamp
import timber.log.Timber
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class PlayServices @Inject constructor(
@ApplicationContext private val context: Context,
private val preferences: Preferences,
) {
fun isAvailable() =
getInstance().isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS
suspend fun requestReview(activity: Activity) {
val now = now()
val installCutoff = preferences.installDate + INSTALL_COOLDOWN
val reviewCutoff = preferences.lastReviewRequest + REVIEW_COOLDOWN
if (installCutoff > now || reviewCutoff > now) {
Timber.d("wait for review request: install=${printTimestamp(installCutoff)} review=${printTimestamp(reviewCutoff)}")
return
}
try {
with(ReviewManagerFactory.create(context)) {
val request = requestReview()
launchReview(activity, request)
preferences.lastReviewRequest = now
}
} catch (e: Exception) {
Timber.e(e)
}
}
companion object {
private val INSTALL_COOLDOWN = TimeUnit.DAYS.toMillis(14)
private val REVIEW_COOLDOWN = TimeUnit.DAYS.toMillis(30)
}
}

@ -48,6 +48,7 @@ import org.tasks.fragments.CommentBarFragment.CommentBarFragmentCallback
import org.tasks.injection.InjectingAppCompatActivity
import org.tasks.intents.TaskIntents.getTaskListIntent
import org.tasks.location.LocationPickerActivity
import org.tasks.play.PlayServices
import org.tasks.preferences.DefaultFilterProvider
import org.tasks.preferences.Preferences
import org.tasks.themes.ColorProvider
@ -56,8 +57,8 @@ import org.tasks.themes.ThemeColor
import org.tasks.ui.DeadlineControlSet.DueDateChangeListener
import org.tasks.ui.EmptyTaskEditFragment.Companion.newEmptyTaskEditFragment
import org.tasks.ui.ListFragment.OnListChanged
import org.tasks.ui.MainActivityEventBus
import org.tasks.ui.MainActivityEvent
import org.tasks.ui.MainActivityEventBus
import org.tasks.ui.NavigationDrawerFragment
import org.tasks.ui.NavigationDrawerFragment.Companion.newNavigationDrawer
import timber.log.Timber
@ -77,6 +78,7 @@ class MainActivity : InjectingAppCompatActivity(), TaskListFragmentCallbackHandl
@Inject lateinit var tagDataDao: TagDataDao
@Inject lateinit var alarmDao: AlarmDao
@Inject lateinit var eventBus: MainActivityEventBus
@Inject lateinit var playServices: PlayServices
private var currentNightMode = 0
private var currentPro = false
@ -109,6 +111,8 @@ class MainActivity : InjectingAppCompatActivity(), TaskListFragmentCallbackHandl
private suspend fun process(event: MainActivityEvent) = when (event) {
is MainActivityEvent.OpenTask ->
onTaskListItemClicked(event.task)
is MainActivityEvent.RequestRating ->
playServices.requestReview(this)
}
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

@ -16,21 +16,17 @@ import org.tasks.billing.PurchaseActivity
import org.tasks.databinding.DialogWhatsNewBinding
import org.tasks.extensions.Context.openUri
import org.tasks.markdown.MarkdownProvider
import org.tasks.preferences.Preferences
import java.io.BufferedReader
import javax.inject.Inject
import kotlin.random.Random
@AndroidEntryPoint
class WhatsNewDialog : DialogFragment() {
@Inject lateinit var dialogBuilder: DialogBuilder
@Inject lateinit var firebase: Firebase
@Inject lateinit var preferences: Preferences
@Inject lateinit var inventory: Inventory
@Inject lateinit var markdownProvider: MarkdownProvider
private var displayedRate = false
private var displayedSubscribe = false
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
@ -43,10 +39,6 @@ class WhatsNewDialog : DialogFragment() {
.markdown(linkify = true, force = true)
.setMarkdown(binding.changelog, text)
val begForSubscription = !inventory.hasPro
val begForRating = !preferences.getBoolean(R.string.p_clicked_rate, false)
&& (!begForSubscription || Random.nextBoolean())
when {
IS_GENERIC -> {
binding.actionQuestion.setText(R.string.enjoying_tasks)
@ -54,13 +46,7 @@ class WhatsNewDialog : DialogFragment() {
binding.actionButton.text = getString(R.string.TLA_menu_donate)
binding.actionButton.setOnClickListener { onDonateClick() }
}
begForRating -> {
displayedRate = true
binding.actionQuestion.setText(R.string.enjoying_tasks)
binding.actionButton.setText(R.string.rate_tasks)
binding.actionButton.setOnClickListener { onRateClick() }
}
begForSubscription -> {
!inventory.hasPro -> {
displayedSubscribe = true
binding.actionQuestion.setText(R.string.tasks_needs_your_support)
binding.actionText.setText(R.string.support_development_subscribe)
@ -95,13 +81,6 @@ class WhatsNewDialog : DialogFragment() {
startActivity(Intent(context, PurchaseActivity::class.java))
}
private fun onRateClick() {
logClick(true)
preferences.setBoolean(R.string.p_clicked_rate, true)
dismiss()
context?.openUri(R.string.market_url)
}
private fun onDonateClick() {
dismiss()
context?.openUri(R.string.url_donate)
@ -116,7 +95,6 @@ class WhatsNewDialog : DialogFragment() {
firebase.logEvent(
R.string.event_whats_new,
Pair(R.string.param_click, click),
Pair(R.string.param_whats_new_display_rate, displayedRate),
Pair(R.string.param_whats_new_display_subscribe, displayedSubscribe),
Pair(R.string.param_user_pro, inventory.hasPro),
)

@ -532,6 +532,10 @@ class Preferences @JvmOverloads constructor(
val isTopAppBar: Boolean
get() = getIntegerFromString(R.string.p_app_bar_position, 1) == 0
var lastReviewRequest: Long
get() = getLong(R.string.p_last_review_request, 0L)
set(value) = setLong(R.string.p_last_review_request, value)
companion object {
private const val PREF_SORT_SORT = "sort_sort" // $NON-NLS-1$

@ -7,5 +7,6 @@ typealias MainActivityEventBus = MutableSharedFlow<MainActivityEvent>
sealed interface MainActivityEvent {
data class OpenTask(val task: Task) : MainActivityEvent
object RequestRating : MainActivityEvent
}

@ -82,6 +82,7 @@ class TaskEditViewModel @Inject constructor(
private val taskCompleter: TaskCompleter,
private val alarmService: AlarmService,
private val taskListEvents: TaskListEventBus,
private val mainActivityEvents: MainActivityEventBus,
) : ViewModel() {
val cleared = MutableLiveData<Event<Boolean>>()
@ -436,6 +437,7 @@ class TaskEditViewModel @Inject constructor(
model.calendarURI?.takeIf { it.isNotBlank() }?.let {
taskListEvents.emit(TaskListEvent.CalendarEventCreated(model.title, it))
}
mainActivityEvents.emit(MainActivityEvent.RequestRating)
}
}

@ -410,7 +410,7 @@
<string name="p_hide_check_button">Hide check button</string>
<string name="p_show_whats_new">show_whats_new</string>
<string name="p_just_updated">just_updated</string>
<string name="p_clicked_rate">clicked_rate</string>
<string name="p_last_review_request">last_review_request</string>
<string name="p_backups_enabled">backups_enabled</string>
<string name="p_backups_ignore_warnings">backups_ignore_warnings</string>
<string name="p_backups_android_backup_enabled">backups_android_backup_enabled</string>

@ -220,6 +220,13 @@
+| +--- com.android.billingclient:billing:3.0.3
+| +--- org.jetbrains.kotlin:kotlin-stdlib:1.4.0 -> 1.6.10 (*)
+| \--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9 -> 1.5.2 (*)
++--- com.google.android.play:core:1.10.3
++--- com.google.android.play:core-ktx:1.8.1
+| +--- com.google.android.play:core:1.8.0 -> 1.10.3
+| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72 -> 1.6.10 (*)
+| +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.6 -> 1.5.2 (*)
+| +--- androidx.core:core:1.1.0 -> 1.7.0 (*)
+| \--- androidx.fragment:fragment:1.1.0 -> 1.4.0 (*)
++--- com.gitlab.abaker:dav4jvm:deb2c9aef8
+| +--- org.jetbrains.kotlin:kotlin-stdlib:1.4.10 -> 1.6.10 (*)
+| \--- org.apache.commons:commons-lang3:3.8.1

Loading…
Cancel
Save