Add support page

This commit is contained in:
Billy Brawner 2019-11-07 10:16:44 -06:00 committed by William Brawner
parent 5d61ccc4fb
commit 070d580044
9 changed files with 206 additions and 33 deletions

View file

@ -17,7 +17,6 @@ try {
keystoreProperties['storePassword'] = "" keystoreProperties['storePassword'] = ""
} }
android { android {
configurations.all { configurations.all {
resolutionStrategy.force 'com.google.code.findbugs:jsr305:3.0.1' resolutionStrategy.force 'com.google.code.findbugs:jsr305:3.0.1'
@ -101,6 +100,7 @@ dependencies {
implementation 'com.android.billingclient:billing:1.2' implementation 'com.android.billingclient:billing:1.2'
implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1' implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'
implementation "androidx.core:core-ktx:1.1.0" implementation "androidx.core:core-ktx:1.1.0"
implementation 'androidx.browser:browser:1.0.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
def coroutines_version = "1.3.0-RC2" def coroutines_version = "1.3.0-RC2"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
@ -136,10 +136,10 @@ task jacocoTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest']) {
def kotlinDebugTree = fileTree(dir: "$project.buildDir/tmp/kotlin-classes/debug", excludes: fileFilter) def kotlinDebugTree = fileTree(dir: "$project.buildDir/tmp/kotlin-classes/debug", excludes: fileFilter)
def mainSrc = "$project.projectDir/src/main/java" def mainSrc = "$project.projectDir/src/main/java"
sourceDirectories = files([mainSrc]) sourceDirectories.setFrom(files([mainSrc]))
classDirectories = files([javaDebugTree, kotlinDebugTree]) classDirectories.setFrom(files([javaDebugTree, kotlinDebugTree]))
executionData = fileTree(dir: project.buildDir, includes: [ executionData.setFrom(fileTree(dir: project.buildDir, includes: [
'jacoco/testDebugUnitTest.exec', 'jacoco/testDebugUnitTest.exec',
'outputs/code-coverage/connected/*coverage.ec' 'outputs/code-coverage/connected/*coverage.ec'
]) ]))
} }

View file

@ -18,10 +18,6 @@
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
tools:ignore="AllowBackup" tools:ignore="AllowBackup"
tools:targetApi="n"> tools:targetApi="n">
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="@string/admob_app_id" />
<activity <activity
android:name=".view.activity.SplashActivity" android:name=".view.activity.SplashActivity"
android:theme="@style/AppTheme.Splash" android:theme="@style/AppTheme.Splash"
@ -68,6 +64,8 @@
android:value=".view.activity.MainActivity" /> android:value=".view.activity.MainActivity" />
</activity> </activity>
<activity android:name=".view.activity.SupportActivity" />
<meta-data <meta-data
android:name="firebase_crashlytics_collection_enabled" android:name="firebase_crashlytics_collection_enabled"
android:value="false" /> android:value="false" />

View file

@ -1,21 +1,61 @@
package com.wbrawner.simplemarkdown.view.activity package com.wbrawner.simplemarkdown.view.activity
import android.content.ActivityNotFoundException
import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.view.MenuItem
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.browser.customtabs.CustomTabsIntent
import com.android.billingclient.api.* import com.android.billingclient.api.*
import java.util.* import com.google.android.material.button.MaterialButton
import com.wbrawner.simplemarkdown.R
import kotlinx.android.synthetic.main.activity_support.*
class SupportActivity : AppCompatActivity(), BillingClientStateListener, PurchasesUpdatedListener { class SupportActivity : AppCompatActivity(), BillingClientStateListener, PurchasesUpdatedListener {
private lateinit var billingClient: BillingClient private lateinit var billingClient: BillingClient
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_support)
setSupportActionBar(toolbar)
setTitle(R.string.support_title)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
billingClient = BillingClient.newBuilder(applicationContext) billingClient = BillingClient.newBuilder(applicationContext)
.setListener(this) .setListener(this)
.build() .build()
billingClient.startConnection(this) billingClient.startConnection(this)
githubButton.setOnClickListener {
CustomTabsIntent.Builder()
.addDefaultShareMenuItem()
.build()
.launchUrl(this@SupportActivity, Uri.parse("https://github.com/wbrawner/SimpleMarkdown"))
}
rateButton.setOnClickListener {
val playStoreIntent = Intent(Intent.ACTION_VIEW)
.apply {
data = Uri.parse("market://details?id=${packageName}")
addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY or
Intent.FLAG_ACTIVITY_NEW_DOCUMENT or
Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
}
try {
startActivity(playStoreIntent)
} catch (ignored: ActivityNotFoundException) {
playStoreIntent.data = Uri.parse("https://play.google.com/store/apps/details?id=${packageName}")
startActivity(playStoreIntent)
}
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
finish()
return true
}
return super.onOptionsItemSelected(item)
} }
override fun onBillingSetupFinished(responseCode: Int) { override fun onBillingSetupFinished(responseCode: Int) {
@ -23,36 +63,47 @@ class SupportActivity : AppCompatActivity(), BillingClientStateListener, Purchas
return return
} }
// The billing client is ready. You can query purchases here. val skuDetails = SkuDetailsParams.newBuilder()
val skuList = ArrayList<String>() .setSkusList(listOf("support_the_developer", "tip_coffee", "tip_beer"))
skuList.add("support_the_developer") .setType(BillingClient.SkuType.INAPP)
val params = SkuDetailsParams.newBuilder() .build()
params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP) billingClient.querySkuDetailsAsync(skuDetails) { skuDetailsResponseCode, skuDetailsList ->
billingClient.querySkuDetailsAsync(params.build()) { responseCode1, skuDetailsList ->
// Process the result. // Process the result.
if (responseCode1 != BillingClient.BillingResponse.OK || skuDetailsList == null) { if (skuDetailsResponseCode != BillingClient.BillingResponse.OK || skuDetailsList.isNullOrEmpty()) {
return@querySkuDetailsAsync return@querySkuDetailsAsync
} }
val skuDetails = skuDetailsList!!.get(0) skuDetailsList.sortedBy { it.priceAmountMicros }.forEach { skuDetails ->
val sku = skuDetails.getSku() val supportButton = MaterialButton(this@SupportActivity)
val price = skuDetails.getPrice() supportButton.text = getString(
Log.d("SimpleMarkdown", R.string.support_button_purchase,
"Got product with sku: " + sku + " and price: " + price + " " + skuDetails.getPriceCurrencyCode()) skuDetails.title,
val flowParams = BillingFlowParams.newBuilder() skuDetails.price
.setSkuDetails(skuDetails) )
.build() supportButton.setOnClickListener {
val responseCode2 = billingClient.launchBillingFlow(this, flowParams) val flowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetails)
.build()
billingClient.launchBillingFlow(this, flowParams)
}
supportButtons.addView(supportButton)
}
} }
} }
override fun onBillingServiceDisconnected() { override fun onBillingServiceDisconnected() {
// TODO: Set a flag and just try again later
billingClient.startConnection(this) billingClient.startConnection(this)
} }
override fun onPurchasesUpdated(responseCode: Int, purchases: List<Purchase>?) { override fun onPurchasesUpdated(responseCode: Int, purchases: List<Purchase>?) {
purchases?.forEach { purchase ->
billingClient.consumeAsync(purchase.purchaseToken) { _, _ ->
Toast.makeText(
this@SupportActivity,
getString(R.string.support_thank_you),
Toast.LENGTH_SHORT
).show()
}
}
} }
} }

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z" />
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@color/colorOnBackground"
android:pathData="M3,18h18v-2L3,16v2zM3,13h18v-2L3,11v2zM3,6v2h18L21,6L3,6z" />
</vector>

View file

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
android:padding="16dp"
app:layout_constraintBottom_toTopOf="@+id/bottomSheet"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/heartIcon"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/ic_favorite_black_24dp"
android:tint="@color/colorAccent"
android:contentDescription="@string/description_heart"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/supportInfoText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:text="@string/support_info"
android:textAlignment="center"
android:textColor="@color/colorOnBackground"
app:layout_constraintTop_toBottomOf="@+id/heartIcon" />
<com.google.android.material.button.MaterialButton
android:id="@+id/githubButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="@color/colorBackgroundGitHub"
android:textColor="@color/colorWhite"
android:text="@string/action_view_github"
app:layout_constraintTop_toBottomOf="@+id/supportInfoText" />
<com.google.android.material.button.MaterialButton
android:id="@+id/rateButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="@color/colorBackgroundPlayStore"
android:textColor="@color/colorWhite"
android:text="@string/action_rate"
app:layout_constraintTop_toBottomOf="@+id/githubButton" />
<LinearLayout
android:id="@+id/supportButtons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@+id/rateButton" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
<com.google.android.material.card.MaterialCardView
android:id="@+id/bottomSheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:translationY="16dp"
app:cardCornerRadius="16dp"
app:cardElevation="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_marginBottom="16dp"
android:background="@color/colorBackground" />
</com.google.android.material.card.MaterialCardView>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -5,4 +5,7 @@
<color name="colorAccent">#d32f2f</color> <color name="colorAccent">#d32f2f</color>
<color name="colorBackground">#FFFFFF</color> <color name="colorBackground">#FFFFFF</color>
<color name="colorOnBackground">#000000</color> <color name="colorOnBackground">#000000</color>
<color name="colorBackgroundGitHub">#24292e</color>
<color name="colorWhite">#FFFFFFFF</color>
<color name="colorBackgroundPlayStore">#689f38</color>
</resources> </resources>

View file

@ -2,8 +2,6 @@
<string name="app_name">Simple Markdown</string> <string name="app_name">Simple Markdown</string>
<string name="app_name_short">Markdown</string> <string name="app_name_short">Markdown</string>
<string name="admob_app_id">ca-app-pub-3319579963502409~4576405307</string>
<string name="action_settings">Settings</string> <string name="action_settings">Settings</string>
<string name="action_help">Help</string> <string name="action_help">Help</string>
<string name="action_edit">Edit</string> <string name="action_edit">Edit</string>
@ -65,6 +63,20 @@
<string name="prompt_save_changes">Would you like to save your changes?</string> <string name="prompt_save_changes">Would you like to save your changes?</string>
<string name="action_discard">Discard</string> <string name="action_discard">Discard</string>
<string name="action_save_as">Save as…</string> <string name="action_save_as">Save as…</string>
<string name="support_info">SimpleMarkdown is a personal project of mine that I develop and
maintain in my free time. I very much appreciate any and all forms of support, whether
that be assistance with designing the app or icons, adding translations for other
languages, contributing to the code, or giving me a tip. If you have any troubles with
SimpleMarkdown, please don\'t hesitate to get in touch, and if you haven\'t already,
please rate the app on the Play Store.\n\nAlso please keep in mind that the in-app
purchases for the tips won\'t unlock any additional functionality in the app.
SimpleMarkdown is and always will be completely free and open source!</string>
<string name="action_view_github">View SimpleMarkdown on GitHub</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="support_thank_you">Thank you so much for your support!</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>
<item>@string/pref_value_dark</item> <item>@string/pref_value_dark</item>

View file

@ -10,7 +10,7 @@ buildscript {
} }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.5.1' classpath 'com.android.tools.build:gradle:3.5.2'
classpath 'com.google.gms:google-services:4.3.2' classpath 'com.google.gms:google-services:4.3.2'
classpath 'io.fabric.tools:gradle:1.31.0' classpath 'io.fabric.tools:gradle:1.31.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"