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:
parent
de9956cbf7
commit
2a0cc4d889
35 changed files with 360 additions and 168 deletions
1
app/.gitignore
vendored
1
app/.gitignore
vendored
|
@ -2,4 +2,3 @@
|
||||||
*.apk
|
*.apk
|
||||||
*.aab
|
*.aab
|
||||||
/release
|
/release
|
||||||
acra.properties
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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"
|
|
||||||
}
|
|
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
package com.wbrawner.simplemarkdown.utility
|
|
||||||
|
|
||||||
interface ErrorHandler {
|
|
||||||
fun enable(enable: Boolean)
|
|
||||||
fun reportException(t: Throwable, message: String? = null)
|
|
||||||
}
|
|
|
@ -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)
|
||||||
}
|
}
|
|
@ -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>
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
2
core/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/build
|
||||||
|
/acra.properties
|
66
core/build.gradle.kts
Normal file
66
core/build.gradle.kts
Normal 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
0
core/consumer-rules.pro
Normal file
21
core/proguard-rules.pro
vendored
Normal file
21
core/proguard-rules.pro
vendored
Normal 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
|
4
core/src/main/AndroidManifest.xml
Normal file
4
core/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
</manifest>
|
|
@ -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
1
free/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/build
|
63
free/build.gradle.kts
Normal file
63
free/build.gradle.kts
Normal 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
0
free/consumer-rules.pro
Normal file
21
free/proguard-rules.pro
vendored
Normal file
21
free/proguard-rules.pro
vendored
Normal 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
|
4
free/src/main/AndroidManifest.xml
Normal file
4
free/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
</manifest>
|
|
@ -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
1
non-free/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/build
|
65
non-free/build.gradle.kts
Normal file
65
non-free/build.gradle.kts
Normal 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")
|
||||||
|
}
|
0
non-free/consumer-rules.pro
Normal file
0
non-free/consumer-rules.pro
Normal file
21
non-free/proguard-rules.pro
vendored
Normal file
21
non-free/proguard-rules.pro
vendored
Normal 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
|
4
non-free/src/main/AndroidManifest.xml
Normal file
4
non-free/src/main/AndroidManifest.xml
Normal 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>
|
|
@ -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
|
|
@ -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 =
|
6
non-free/src/main/res/values/strings.xml
Normal file
6
non-free/src/main/res/values/strings.xml
Normal 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>
|
|
@ -1 +1 @@
|
||||||
include(":app")
|
include(":app", ":core", ":free", "non-free")
|
||||||
|
|
Loading…
Reference in a new issue