Split free & play code into separate gradle modules

This will hopefully enable me to use the gradle build health plugin, and still be compliant with F-Droid's policies. A consequence of this is that I had to go back to ACRA for error reporting, since I couldn't find a way to keep Firebase's gradle plugins on a library module instead of the main app module
This commit is contained in:
William Brawner 2024-05-18 22:13:39 -06:00
parent de9956cbf7
commit 2a0cc4d889
Signed by: wbrawner
GPG key ID: 8FF12381C6C90D35
35 changed files with 360 additions and 168 deletions

1
app/.gitignore vendored
View file

@ -2,4 +2,3 @@
*.apk *.apk
*.aab *.aab
/release /release
acra.properties

View file

@ -1,6 +1,5 @@
import java.io.FileInputStream import java.io.FileInputStream
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.util.Locale
import java.util.Properties import java.util.Properties
plugins { plugins {
@ -111,6 +110,8 @@ play {
} }
dependencies { dependencies {
"freeImplementation"(project(":free"))
"playImplementation"(project(":non-free"))
implementation("androidx.compose.material3:material3-window-size-class-android:1.2.0") implementation("androidx.compose.material3:material3-window-size-class-android:1.2.0")
val navigationVersion = "2.7.2" val navigationVersion = "2.7.2"
implementation("androidx.navigation:navigation-fragment-ktx:$navigationVersion") implementation("androidx.navigation:navigation-fragment-ktx:$navigationVersion")
@ -134,7 +135,6 @@ dependencies {
implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("com.google.android.material:material:1.9.0") implementation("com.google.android.material:material:1.9.0")
implementation("androidx.legacy:legacy-support-v13:1.0.0") implementation("androidx.legacy:legacy-support-v13:1.0.0")
implementation("com.jakewharton.timber:timber:5.0.1")
implementation("androidx.core:core-ktx:1.12.0") implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.browser:browser:1.6.0") implementation("androidx.browser:browser:1.6.0")
val commonMarkVersion = "0.22.0" val commonMarkVersion = "0.22.0"
@ -169,21 +169,6 @@ dependencies {
implementation("eu.crydee:syllable-counter:4.0.2") implementation("eu.crydee:syllable-counter:4.0.2")
} }
android.productFlavors.forEach { flavor ->
if (gradle.startParameter.taskRequests.toString().lowercase(Locale.getDefault()).contains(flavor.name)
&& flavor.name == "play"
) {
apply(plugin = "com.google.gms.google-services")
apply(plugin = "com.google.firebase.crashlytics")
dependencies {
implementation("com.android.billingclient:billing:6.0.1")
implementation("com.google.android.play:core-ktx:1.8.1")
implementation("com.google.firebase:firebase-crashlytics:18.4.1")
}
}
}
fladle { fladle {
variant.set("playDebug") variant.set("playDebug")
useOrchestrator.set(true) useOrchestrator.set(true)

View file

@ -1,78 +0,0 @@
{
"project_info": {
"project_number": "318641233555",
"firebase_url": "https://simplemarkdown.firebaseio.com",
"project_id": "simplemarkdown",
"storage_bucket": "simplemarkdown.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:318641233555:android:09d865cad87e3b5f",
"android_client_info": {
"package_name": "com.wbrawner.simplemarkdown"
}
},
"oauth_client": [
{
"client_id": "318641233555-83n2k1mqhokf0b7lhccqiva9pspgripq.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.wbrawner.simplemarkdown",
"certificate_hash": "710e37c230689bc259fc6e2e032e2f56956f8d33"
}
},
{
"client_id": "318641233555-a5rm2cqqf66jc9j5nmg3ttepdt2iaeno.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyBDMcXg-10NsXLDKJRtj5WnXoHrwg3m9Os"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "318641233555-a5rm2cqqf66jc9j5nmg3ttepdt2iaeno.apps.googleusercontent.com",
"client_type": 3
}
]
}
},
"admob_app_id": "ca-app-pub-3319579963502409~4576405307"
},
{
"client_info": {
"mobilesdk_app_id": "1:318641233555:android:5dfb62206717437e",
"android_client_info": {
"package_name": "com.wbrawner.simplemarkdown.samsung"
}
},
"oauth_client": [
{
"client_id": "318641233555-a5rm2cqqf66jc9j5nmg3ttepdt2iaeno.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyBDMcXg-10NsXLDKJRtj5WnXoHrwg3m9Os"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "318641233555-a5rm2cqqf66jc9j5nmg3ttepdt2iaeno.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
}
],
"configuration_version": "1"
}

View file

@ -1,23 +0,0 @@
package com.wbrawner.simplemarkdown.utility
import timber.log.Timber
import kotlin.reflect.KProperty
class errorHandlerImpl {
operator fun getValue(thisRef: Any, property: KProperty<*>): ErrorHandler {
return implementation
}
companion object {
// Default no-op error handler for debugging
val implementation: ErrorHandler = object : ErrorHandler {
override fun enable(enable: Boolean) {
// no-op
}
override fun reportException(t: Throwable, message: String?) {
Timber.e(t, "Caught non-fatal exception. Message: $message")
}
}
}
}

View file

@ -41,6 +41,7 @@ import com.wbrawner.simplemarkdown.ui.SettingsScreen
import com.wbrawner.simplemarkdown.ui.SupportScreen import com.wbrawner.simplemarkdown.ui.SupportScreen
import com.wbrawner.simplemarkdown.ui.theme.SimpleMarkdownTheme import com.wbrawner.simplemarkdown.ui.theme.SimpleMarkdownTheme
import com.wbrawner.simplemarkdown.utility.Preference import com.wbrawner.simplemarkdown.utility.Preference
import org.acra.ACRA
class MainActivity : AppCompatActivity(), ActivityCompat.OnRequestPermissionsResultCallback { class MainActivity : AppCompatActivity(), ActivityCompat.OnRequestPermissionsResultCallback {
private val viewModel: MarkdownViewModel by viewModels { private val viewModel: MarkdownViewModel by viewModels {
@ -84,6 +85,11 @@ class MainActivity : AppCompatActivity(), ActivityCompat.OnRequestPermissionsRes
} }
AppCompatDelegate.setDefaultNightMode(darkMode) AppCompatDelegate.setDefaultNightMode(darkMode)
} }
val errorReporterPreference by preferenceHelper.observe<Boolean>(Preference.ERROR_REPORTS_ENABLED)
.collectAsState()
LaunchedEffect(errorReporterPreference) {
ACRA.errorReporter.setEnabled(errorReporterPreference)
}
val windowSizeClass = calculateWindowSizeClass(this) val windowSizeClass = calculateWindowSizeClass(this)
SimpleMarkdownTheme { SimpleMarkdownTheme {
val navController = rememberNavController() val navController = rememberNavController()

View file

@ -2,6 +2,7 @@ package com.wbrawner.simplemarkdown
import android.app.Application import android.app.Application
import android.os.StrictMode import android.os.StrictMode
import com.wbrawner.simplemarkdown.core.ErrorReporterTree
import com.wbrawner.simplemarkdown.utility.AndroidFileHelper import com.wbrawner.simplemarkdown.utility.AndroidFileHelper
import com.wbrawner.simplemarkdown.utility.AndroidPreferenceHelper import com.wbrawner.simplemarkdown.utility.AndroidPreferenceHelper
import com.wbrawner.simplemarkdown.utility.FileHelper import com.wbrawner.simplemarkdown.utility.FileHelper
@ -34,6 +35,7 @@ class MarkdownApplication : Application() {
} }
} }
} }
Timber.plant(ErrorReporterTree.create(this))
super.onCreate() super.onCreate()
ReviewHelper.init(this) ReviewHelper.init(this)
fileHelper = AndroidFileHelper(this) fileHelper = AndroidFileHelper(this)

View file

@ -29,6 +29,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.navigation.NavController import androidx.navigation.NavController
import com.wbrawner.simplemarkdown.BuildConfig
import com.wbrawner.simplemarkdown.ui.theme.SimpleMarkdownTheme import com.wbrawner.simplemarkdown.ui.theme.SimpleMarkdownTheme
import com.wbrawner.simplemarkdown.utility.Preference import com.wbrawner.simplemarkdown.utility.Preference
import com.wbrawner.simplemarkdown.utility.PreferenceHelper import com.wbrawner.simplemarkdown.utility.PreferenceHelper
@ -79,6 +80,25 @@ fun SettingsScreen(navController: NavController, preferenceHelper: PreferenceHel
preference = Preference.READABILITY_ENABLED, preference = Preference.READABILITY_ENABLED,
preferenceHelper = preferenceHelper preferenceHelper = preferenceHelper
) )
if (BuildConfig.DEBUG) {
Row(modifier = Modifier
.fillMaxWidth()
.clickable {
error("Forced crash")
}
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically) {
Column(verticalArrangement = Arrangement.Center) {
Text(text = "Force a crash", style = MaterialTheme.typography.bodyLarge)
Text(
text = "Purposefully crash the app for testing purposes",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
}
} }
} }
} }

View file

@ -1,6 +0,0 @@
package com.wbrawner.simplemarkdown.utility
interface ErrorHandler {
fun enable(enable: Boolean)
fun reportException(t: Throwable, message: String? = null)
}

View file

@ -62,6 +62,6 @@ enum class Preference(val key: String, val default: Any?) {
AUTOSAVE_URI("autosave.uri", null), AUTOSAVE_URI("autosave.uri", null),
CUSTOM_CSS("pref.custom_css", null), CUSTOM_CSS("pref.custom_css", null),
DARK_MODE("darkMode", "Auto"), DARK_MODE("darkMode", "Auto"),
ERROR_REPORTS_ENABLED("crashlytics.enable", true), ERROR_REPORTS_ENABLED("acra.enable", true),
READABILITY_ENABLED("readability.enable", false) READABILITY_ENABLED("readability.enable", false)
} }

View file

@ -72,9 +72,7 @@
SimpleMarkdown is and always will be completely free and open source!</string> SimpleMarkdown is and always will be completely free and open source!</string>
<string name="action_view_github">View SimpleMarkdown on GitHub</string> <string name="action_view_github">View SimpleMarkdown on GitHub</string>
<string name="support_title">Support SimpleMarkdown</string> <string name="support_title">Support SimpleMarkdown</string>
<string name="support_button_purchase">%1$s %2$s</string>
<string name="action_rate">Rate SimpleMarkdown</string> <string name="action_rate">Rate SimpleMarkdown</string>
<string name="support_thank_you">Thank you so much for your support!</string>
<string name="description_heart">Heart</string> <string name="description_heart">Heart</string>
<string-array name="pref_entries_dark_mode"> <string-array name="pref_entries_dark_mode">
<item>@string/pref_value_light</item> <item>@string/pref_value_light</item>

View file

@ -1,32 +0,0 @@
package com.wbrawner.simplemarkdown.utility
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.wbrawner.simplemarkdown.BuildConfig
import timber.log.Timber
import kotlin.reflect.KProperty
class CrashlyticsErrorHandler : ErrorHandler {
private val crashlytics = FirebaseCrashlytics.getInstance()
override fun enable(enable: Boolean) {
crashlytics.setCrashlyticsCollectionEnabled(enable)
}
override fun reportException(t: Throwable, message: String?) {
@Suppress("ConstantConditionIf")
if (BuildConfig.DEBUG) {
Timber.e(t, "Caught exception: $message")
}
crashlytics.recordException(t)
}
}
class errorHandlerImpl {
operator fun getValue(thisRef: Any, property: KProperty<*>): ErrorHandler {
return impl
}
companion object {
val impl = CrashlyticsErrorHandler()
}
}

View file

@ -13,6 +13,10 @@ buildscript {
} }
} }
plugins {
id("com.autonomousapps.dependency-analysis") version "1.31.0"
}
allprojects { allprojects {
repositories { repositories {
google() google()

2
core/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/build
/acra.properties

66
core/build.gradle.kts Normal file
View file

@ -0,0 +1,66 @@
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.util.Properties
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
}
val acraProperties = Properties()
try {
val acraPropertiesFile = project.file("acra.properties")
acraProperties.load(FileInputStream(acraPropertiesFile))
} catch (ignored: FileNotFoundException) {
logger.warn("Unable to load ACRA properties. Error reporting won't be available")
acraProperties["url"] = ""
acraProperties["user"] = ""
acraProperties["pass"] = ""
}
android {
namespace = "com.wbrawner.simplemarkdown.core"
compileSdk = 34
defaultConfig {
minSdk = 23
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
buildConfigField("String", "ACRA_URL", "\"${acraProperties["url"]}\"")
buildConfigField("String", "ACRA_USER", "\"${acraProperties["user"]}\"")
buildConfigField("String", "ACRA_PASS", "\"${acraProperties["pass"]}\"")
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
val acraVersion = "5.11.3"
api("ch.acra:acra-core:$acraVersion")
implementation("ch.acra:acra-http:$acraVersion")
implementation("ch.acra:acra-limiter:$acraVersion")
implementation("ch.acra:acra-advanced-scheduler:$acraVersion")
api("com.jakewharton.timber:timber:5.0.1")
implementation("androidx.core:core-ktx:1.13.1")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.12.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}

0
core/consumer-rules.pro Normal file
View file

21
core/proguard-rules.pro vendored Normal file
View file

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>

View file

@ -0,0 +1,37 @@
package com.wbrawner.simplemarkdown.core
import android.app.Application
import android.util.Log
import org.acra.ACRA
import org.acra.config.httpSender
import org.acra.data.StringFormat
import org.acra.ktx.initAcra
import org.acra.ktx.sendSilentlyWithAcra
import org.acra.sender.HttpSender
import timber.log.Timber
class ErrorReporterTree private constructor(): Timber.Tree() {
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
if (priority != Log.ERROR) return
t?.sendSilentlyWithAcra()
}
companion object {
fun create(application: Application): ErrorReporterTree {
application.createErrorReporterTree()
return ErrorReporterTree()
}
}
}
private fun Application.createErrorReporterTree() {
initAcra {
reportFormat = StringFormat.JSON
httpSender {
uri = "${BuildConfig.ACRA_URL}/report" /*best guess, you may need to adjust this*/
basicAuthLogin = BuildConfig.ACRA_USER
basicAuthPassword = BuildConfig.ACRA_PASS
httpMethod = HttpSender.Method.POST
}
}
}

1
free/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

63
free/build.gradle.kts Normal file
View file

@ -0,0 +1,63 @@
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
}
android {
namespace = "com.wbrawner.simplemarkdown.free"
compileSdk = 34
defaultConfig {
minSdk = 23
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.3"
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
api(project(":core"))
implementation("androidx.core:core-ktx:1.13.1")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.12.0")
implementation("com.jakewharton.timber:timber:5.0.1")
val composeBom = platform("androidx.compose:compose-bom:2023.08.00")
implementation(composeBom)
androidTestImplementation(composeBom)
implementation("androidx.compose.runtime:runtime")
implementation("androidx.compose.ui:ui")
implementation("androidx.activity:activity-compose")
implementation("androidx.compose.foundation:foundation")
implementation("androidx.compose.foundation:foundation-layout")
implementation("androidx.compose.material:material")
implementation("androidx.compose.runtime:runtime-livedata")
implementation("androidx.compose.ui:ui-tooling")
implementation("androidx.compose.material3:material3")
implementation("androidx.compose.material:material-icons-extended")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}

0
free/consumer-rules.pro Normal file
View file

21
free/proguard-rules.pro vendored Normal file
View file

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>

View file

@ -2,5 +2,6 @@ package com.wbrawner.simplemarkdown.utility
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
// TODO: Link out to liberapay, GH Sponsors, PayPal, etc
@Composable @Composable
fun SupportLinks() {} fun SupportLinks() {}

1
non-free/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

65
non-free/build.gradle.kts Normal file
View file

@ -0,0 +1,65 @@
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
}
android {
namespace = "com.wbrawner.simplemarkdown"
compileSdk = 34
defaultConfig {
minSdk = 23
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.3"
}
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
api(project(":core"))
implementation("androidx.core:core-ktx:1.13.1")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("androidx.preference:preference-ktx:1.2.1")
implementation("com.android.billingclient:billing:6.0.1")
implementation("com.google.android.play:core-ktx:1.8.1")
implementation("com.jakewharton.timber:timber:5.0.1")
val composeBom = platform("androidx.compose:compose-bom:2023.08.00")
implementation(composeBom)
androidTestImplementation(composeBom)
implementation("androidx.compose.runtime:runtime")
implementation("androidx.compose.ui:ui")
implementation("androidx.activity:activity-compose")
implementation("androidx.compose.foundation:foundation")
implementation("androidx.compose.foundation:foundation-layout")
implementation("androidx.compose.material:material")
implementation("androidx.compose.runtime:runtime-livedata")
implementation("androidx.compose.ui:ui-tooling")
implementation("androidx.compose.material3:material3")
implementation("androidx.compose.material:material-icons-extended")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}

View file

21
non-free/proguard-rules.pro vendored Normal file
View file

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="com.android.vending.BILLING" />
</manifest>

View file

@ -9,7 +9,6 @@ import androidx.core.content.edit
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.android.play.core.review.ReviewManager import com.google.android.play.core.review.ReviewManager
import com.google.android.play.core.review.ReviewManagerFactory import com.google.android.play.core.review.ReviewManagerFactory
import com.wbrawner.simplemarkdown.MainActivity
import timber.log.Timber import timber.log.Timber
private const val KEY_TIME_IN_APP = "timeInApp" private const val KEY_TIME_IN_APP = "timeInApp"
@ -41,13 +40,12 @@ object ReviewHelper : Application.ActivityLifecycleCallbacks {
fun init( fun init(
application: Application, application: Application,
sharedPreferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(application), sharedPreferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(application),
reviewManager: ReviewManager = ReviewManagerFactory.create(application),
timber: Timber.Tree = Timber.asTree() timber: Timber.Tree = Timber.asTree()
) { ) {
this.application = application reviewManager = ReviewManagerFactory.create(application)
this.sharedPreferences = sharedPreferences ReviewHelper.application = application
this.reviewManager = reviewManager ReviewHelper.sharedPreferences = sharedPreferences
this.timber = timber ReviewHelper.timber = timber
if (sharedPreferences.getLong(KEY_TIME_IN_APP, 0L) == -1L) { 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 // We've already prompted the user for the review so let's not be annoying about it
timber.i("User already prompted for review, not configuring ReviewHelper") timber.i("User already prompted for review, not configuring ReviewHelper")
@ -64,7 +62,7 @@ object ReviewHelper : Application.ActivityLifecycleCallbacks {
if (activityCount++ == 0) { if (activityCount++ == 0) {
activeTime = SystemClock.elapsedRealtime() activeTime = SystemClock.elapsedRealtime()
} }
if (activity !is MainActivity || sharedPreferences.getLong(KEY_TIME_IN_APP, 0L) < TIME_TO_PROMPT) { if (sharedPreferences.getLong(KEY_TIME_IN_APP, 0L) < TIME_TO_PROMPT) {
// Not ready to prompt just yet // Not ready to prompt just yet
timber.v("Not ready to prompt user for review yet") timber.v("Not ready to prompt user for review yet")
return return

View file

@ -22,6 +22,7 @@ import com.android.billingclient.api.ConsumeParams
import com.android.billingclient.api.ProductDetails import com.android.billingclient.api.ProductDetails
import com.android.billingclient.api.QueryProductDetailsParams import com.android.billingclient.api.QueryProductDetailsParams
import com.wbrawner.simplemarkdown.R import com.wbrawner.simplemarkdown.R
import timber.log.Timber
@Composable @Composable
fun SupportLinks() { fun SupportLinks() {
@ -68,6 +69,7 @@ fun SupportLinks() {
.build() .build()
billingClient?.queryProductDetailsAsync(productDetailsQuery) { result, productDetails -> billingClient?.queryProductDetailsAsync(productDetailsQuery) { result, productDetails ->
if (result.responseCode != BillingClient.BillingResponseCode.OK || productDetails.isEmpty()) { if (result.responseCode != BillingClient.BillingResponseCode.OK || productDetails.isEmpty()) {
Timber.w("Failed to load product details: ${result.debugMessage}")
return@queryProductDetailsAsync return@queryProductDetailsAsync
} }
products = products =

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="support_button_purchase">%1$s %2$s</string>
<string name="support_thank_you">Thank you so much for your support!</string>
<string name="description_heart">Heart</string>
</resources>

View file

@ -1 +1 @@
include(":app") include(":app", ":core", ":free", "non-free")