Skip to content

Commit d3c39aa

Browse files
authored
Merge pull request #630 from cryptomator/feature/freemium
Add freemium model with 30-day trial, Google Play IAP, and write-access gating
2 parents dc3e70f + 4a5f1ba commit d3c39aa

100 files changed

Lines changed: 5636 additions & 460 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

buildsystem/dependencies.gradle

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ ext {
105105

106106
recyclerViewFastScrollVersion = '2.0.1'
107107

108+
billingclientVersion = '8.2.1'
109+
108110
// testing dependencies
109111

110112
jUnitVersion = '5.11.4'
@@ -202,7 +204,8 @@ ext {
202204
zxcvbn : "com.nulab-inc:zxcvbn:${zxcvbnVersion}",
203205
scaleImageView : "com.github.cryptomator:subsampling-scale-image-view:${scaleImageViewVersion}",
204206
lruFileCache : "com.github.solkin:disk-lru-cache:${lruFileCacheVersion}",
205-
jsonWebToken : "com.auth0:java-jwt:${jsonWebTokenVersion}"
207+
jsonWebToken : "com.auth0:java-jwt:${jsonWebTokenVersion}",
208+
billing : "com.android.billingclient:billing:${billingclientVersion}"
206209
]
207210

208211
}

data/build.gradle

Lines changed: 33 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ android {
4949
dimension "version"
5050
}
5151

52+
playstoreiap {
53+
dimension "version"
54+
}
55+
5256
apkstore {
5357
dimension "version"
5458
}
@@ -71,6 +75,10 @@ android {
7175
java.srcDirs = ['src/main/java/', 'src/apiKey/java/', 'src/apkStorePlaystore/java/']
7276
}
7377

78+
playstoreiap {
79+
java.srcDirs = ['src/main/java/', 'src/apiKey/java/', 'src/apkStorePlaystore/java/']
80+
}
81+
7482
apkstore {
7583
java.srcDirs = ['src/main/java/', 'src/apiKey/java/', 'src/apkStorePlaystore/java/']
7684
}
@@ -89,7 +97,7 @@ android {
8997
}
9098
packagingOptions {
9199
resources {
92-
excludes += ['META-INF/DEPENDENCIES', 'META-INF/NOTICE.md', 'META-INF/INDEX.LIST']
100+
excludes += ['META-INF/DEPENDENCIES', 'META-INF/NOTICE.md', 'META-INF/INDEX.LIST', 'META-INF/versions/9/OSGI-INF/MANIFEST.MF']
93101
}
94102
}
95103

@@ -102,7 +110,7 @@ android {
102110
}
103111

104112
greendao {
105-
schemaVersion 13
113+
schemaVersion 14
106114
}
107115

108116
configurations.all {
@@ -112,13 +120,20 @@ configurations.all {
112120

113121
dependencies {
114122
def dependencies = rootProject.ext.dependencies
123+
def cloudFlavors = ['playstore', 'playstoreiap', 'apkstore', 'fdroid', 'accrescent']
124+
def googleFlavors = ['playstore', 'playstoreiap', 'apkstore']
125+
def addToFlavors = { flavors, dep, Closure config = null ->
126+
flavors.each { flavor ->
127+
if (config) {
128+
add("${flavor}Implementation", dep, config)
129+
} else {
130+
add("${flavor}Implementation", dep)
131+
}
132+
}
133+
}
115134

116135
implementation project(':domain')
117136
implementation project(':util')
118-
playstoreImplementation dependencies.pcloud
119-
apkstoreImplementation dependencies.pcloud
120-
fdroidImplementation dependencies.pcloud
121-
accrescentImplementation dependencies.pcloud
122137

123138
coreLibraryDesugaring dependencies.coreDesugaring
124139

@@ -134,63 +149,28 @@ dependencies {
134149
implementation dependencies.jsonWebToken
135150

136151
// cloud
137-
playstoreImplementation dependencies.dropboxCore
138-
playstoreImplementation dependencies.dropboxAndroid
139-
apkstoreImplementation dependencies.dropboxCore
140-
apkstoreImplementation dependencies.dropboxAndroid
141-
fdroidImplementation dependencies.dropboxCore
142-
fdroidImplementation dependencies.dropboxAndroid
143-
accrescentImplementation dependencies.dropboxCore
144-
accrescentImplementation dependencies.dropboxAndroid
145-
146-
147-
playstoreImplementation dependencies.msgraphAuth
148-
apkstoreImplementation dependencies.msgraphAuth
149-
fdroidImplementation dependencies.msgraphAuth
150-
accrescentImplementation dependencies.msgraphAuth
151-
playstoreImplementation dependencies.msgraph
152-
apkstoreImplementation dependencies.msgraph
153-
fdroidImplementation dependencies.msgraph
154-
accrescentImplementation dependencies.msgraph
152+
addToFlavors(cloudFlavors, dependencies.dropboxCore)
153+
addToFlavors(cloudFlavors, dependencies.dropboxAndroid)
154+
155+
addToFlavors(cloudFlavors, dependencies.msgraphAuth)
156+
addToFlavors(cloudFlavors, dependencies.msgraph)
157+
158+
addToFlavors(cloudFlavors, dependencies.pcloud)
155159

156160
implementation dependencies.stax
157161
api dependencies.minIo
158162

159-
playstoreImplementation(dependencies.googlePlayServicesAuth) {
163+
addToFlavors(googleFlavors, dependencies.googlePlayServicesAuth) {
160164
exclude module: 'guava-jdk5'
161165
exclude module: 'httpclient'
162-
exclude module: 'googlehttpclient'
163166
exclude group: "com.google.http-client", module: "google-http-client"
164167
}
165-
apkstoreImplementation(dependencies.googlePlayServicesAuth) {
168+
addToFlavors(googleFlavors, dependencies.googleApiServicesDrive) {
166169
exclude module: 'guava-jdk5'
167170
exclude module: 'httpclient'
168-
exclude module: "google-http-client"
169171
exclude group: "com.google.http-client", module: "google-http-client"
170172
}
171-
172-
playstoreImplementation(dependencies.googleApiServicesDrive) {
173-
exclude module: 'guava-jdk5'
174-
exclude module: 'httpclient'
175-
exclude module: 'googlehttpclient'
176-
exclude group: "com.google.http-client", module: "google-http-client"
177-
}
178-
apkstoreImplementation(dependencies.googleApiServicesDrive) {
179-
exclude module: 'guava-jdk5'
180-
exclude module: 'httpclient'
181-
exclude module: "google-http-client"
182-
exclude group: "com.google.http-client", module: "google-http-client"
183-
}
184-
185-
playstoreImplementation(dependencies.googleApiClientAndroid) {
186-
exclude module: 'guava-jdk5'
187-
exclude module: 'httpclient'
188-
exclude module: "google-http-client"
189-
exclude module: "jetified-google-http-client"
190-
exclude group: "com.google.http-client", module: "google-http-client"
191-
exclude group: "com.google.http-client", module: "jetified-google-http-client"
192-
}
193-
apkstoreImplementation(dependencies.googleApiClientAndroid) {
173+
addToFlavors(googleFlavors + ['accrescent'], dependencies.googleApiClientAndroid) {
194174
exclude module: 'guava-jdk5'
195175
exclude module: 'httpclient'
196176
exclude module: "google-http-client"
@@ -199,10 +179,8 @@ dependencies {
199179
exclude group: "com.google.http-client", module: "jetified-google-http-client"
200180
}
201181

202-
playstoreImplementation dependencies.trackingFreeGoogleCLient
203-
apkstoreImplementation dependencies.trackingFreeGoogleCLient
204-
playstoreImplementation dependencies.trackingFreeGoogleAndroidCLient
205-
apkstoreImplementation dependencies.trackingFreeGoogleAndroidCLient
182+
addToFlavors(googleFlavors, dependencies.trackingFreeGoogleCLient)
183+
addToFlavors(googleFlavors + ['accrescent'], dependencies.trackingFreeGoogleAndroidCLient)
206184

207185
// rest
208186
implementation dependencies.rxJava

data/src/androidTest/java/org/cryptomator/data/db/UpgradeDatabaseTest.kt

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
66
import androidx.test.filters.SmallTest
77
import androidx.test.platform.app.InstrumentationRegistry
88
import com.google.common.base.Optional
9+
import org.cryptomator.data.BuildConfig
910
import org.cryptomator.data.db.entities.CloudEntityDao
1011
import org.cryptomator.data.db.entities.UpdateCheckEntityDao
1112
import org.cryptomator.data.db.entities.VaultEntityDao
1213
import org.cryptomator.domain.CloudType
14+
import org.cryptomator.util.FlavorConfig
1315
import org.cryptomator.util.SharedPreferencesHandler
1416
import org.cryptomator.util.crypto.CredentialCryptor
1517
import org.cryptomator.util.crypto.CryptoMode
@@ -28,17 +30,19 @@ import org.junit.runner.RunWith
2830
class UpgradeDatabaseTest {
2931

3032
private val context = InstrumentationRegistry.getInstrumentation().context
31-
private val sharedPreferencesHandler = SharedPreferencesHandler(context)
3233
private lateinit var db: Database
34+
private lateinit var sharedPreferencesHandler: SharedPreferencesHandler
3335

3436
@Before
3537
fun setup() {
3638
db = StandardDatabase(SQLiteDatabase.create(null))
39+
sharedPreferencesHandler = SharedPreferencesHandler(context)
3740
}
3841

3942
@After
4043
fun tearDown() {
4144
db.close()
45+
sharedPreferencesHandler.removeAllEntries()
4246
}
4347

4448
@Test
@@ -56,6 +60,7 @@ class UpgradeDatabaseTest {
5660
Upgrade10To11().applyTo(db, 10)
5761
Upgrade11To12(sharedPreferencesHandler).applyTo(db, 11)
5862
Upgrade12To13(context).applyTo(db, 12)
63+
Upgrade13To14(sharedPreferencesHandler).applyTo(db, 13)
5964

6065
CloudEntityDao(DaoConfig(db, CloudEntityDao::class.java)).loadAll()
6166
VaultEntityDao(DaoConfig(db, VaultEntityDao::class.java)).loadAll()
@@ -839,7 +844,6 @@ class UpgradeDatabaseTest {
839844
}
840845
}
841846

842-
843847
@Test
844848
fun upgrade12To13OneDrive() {
845849
Upgrade0To1().applyTo(db, 0)
@@ -951,4 +955,99 @@ class UpgradeDatabaseTest {
951955
Assert.assertThat(it.getString(it.getColumnIndex("ACCESS_TOKEN_CRYPTO_MODE")), CoreMatchers.`is`(CryptoMode.GCM.name))
952956
}
953957
}
958+
959+
@Test
960+
fun upgrade13To14ExistingLicense() {
961+
Upgrade0To1().applyTo(db, 0)
962+
Upgrade1To2().applyTo(db, 1)
963+
Upgrade2To3(context).applyTo(db, 2)
964+
Upgrade3To4().applyTo(db, 3)
965+
Upgrade4To5().applyTo(db, 4)
966+
Upgrade5To6().applyTo(db, 5)
967+
Upgrade6To7().applyTo(db, 6)
968+
Upgrade7To8().applyTo(db, 7)
969+
Upgrade8To9(sharedPreferencesHandler).applyTo(db, 8)
970+
Upgrade9To10(sharedPreferencesHandler).applyTo(db, 9)
971+
Upgrade10To11().applyTo(db, 10)
972+
Upgrade11To12(sharedPreferencesHandler).applyTo(db, 11)
973+
Upgrade12To13(context).applyTo(db, 12)
974+
975+
val licenseToken = "licenseToken"
976+
val releaseNote = "releaseNote"
977+
val version = "version"
978+
val urlApk = "urlApk"
979+
val apkSha256 = "apkSha256"
980+
val urlReleaseNote = "urlReleaseNote"
981+
982+
Sql.update("UPDATE_CHECK_ENTITY")
983+
.set("LICENSE_TOKEN", Sql.toString(licenseToken))
984+
.set("RELEASE_NOTE", Sql.toString(releaseNote))
985+
.set("VERSION", Sql.toString(version))
986+
.set("URL_TO_APK", Sql.toString(urlApk))
987+
.set("APK_SHA256", Sql.toString(apkSha256))
988+
.set("URL_TO_RELEASE_NOTE", Sql.toString(urlReleaseNote))
989+
.executeOn(db)
990+
991+
Upgrade13To14(sharedPreferencesHandler).applyTo(db, 13)
992+
993+
Assert.assertThat(sharedPreferencesHandler.hasCompletedWelcomeFlow(), CoreMatchers.`is`(true))
994+
if (!FlavorConfig.isPremiumFlavor) {
995+
Assert.assertThat(sharedPreferencesHandler.licenseToken(), CoreMatchers.`is`(licenseToken))
996+
}
997+
998+
Sql.query("UPDATE_CHECK_ENTITY").executeOn(db).use {
999+
it.moveToFirst()
1000+
Assert.assertThat(it.getString(it.getColumnIndex("RELEASE_NOTE")), CoreMatchers.`is`(releaseNote))
1001+
Assert.assertThat(it.getString(it.getColumnIndex("VERSION")), CoreMatchers.`is`(version))
1002+
Assert.assertThat(it.getString(it.getColumnIndex("URL_TO_APK")), CoreMatchers.`is`(urlApk))
1003+
Assert.assertThat(it.getString(it.getColumnIndex("APK_SHA256")), CoreMatchers.`is`(apkSha256))
1004+
Assert.assertThat(it.getString(it.getColumnIndex("URL_TO_RELEASE_NOTE")), CoreMatchers.`is`(urlReleaseNote))
1005+
}
1006+
}
1007+
1008+
1009+
@Test
1010+
fun upgrade13To14NoLicense() {
1011+
Upgrade0To1().applyTo(db, 0)
1012+
Upgrade1To2().applyTo(db, 1)
1013+
Upgrade2To3(context).applyTo(db, 2)
1014+
Upgrade3To4().applyTo(db, 3)
1015+
Upgrade4To5().applyTo(db, 4)
1016+
Upgrade5To6().applyTo(db, 5)
1017+
Upgrade6To7().applyTo(db, 6)
1018+
Upgrade7To8().applyTo(db, 7)
1019+
Upgrade8To9(sharedPreferencesHandler).applyTo(db, 8)
1020+
Upgrade9To10(sharedPreferencesHandler).applyTo(db, 9)
1021+
Upgrade10To11().applyTo(db, 10)
1022+
Upgrade11To12(sharedPreferencesHandler).applyTo(db, 11)
1023+
Upgrade12To13(context).applyTo(db, 12)
1024+
1025+
val releaseNote = "releaseNote"
1026+
val version = "version"
1027+
val urlApk = "urlApk"
1028+
val apkSha256 = "apkSha256"
1029+
val urlReleaseNote = "urlReleaseNote"
1030+
1031+
Sql.update("UPDATE_CHECK_ENTITY")
1032+
.set("RELEASE_NOTE", Sql.toString(releaseNote))
1033+
.set("VERSION", Sql.toString(version))
1034+
.set("URL_TO_APK", Sql.toString(urlApk))
1035+
.set("APK_SHA256", Sql.toString(apkSha256))
1036+
.set("URL_TO_RELEASE_NOTE", Sql.toString(urlReleaseNote))
1037+
.executeOn(db)
1038+
1039+
Upgrade13To14(sharedPreferencesHandler).applyTo(db, 13)
1040+
1041+
Assert.assertThat(sharedPreferencesHandler.hasCompletedWelcomeFlow(), CoreMatchers.`is`(true))
1042+
Assert.assertThat(sharedPreferencesHandler.licenseToken(), CoreMatchers.`is`(""))
1043+
1044+
Sql.query("UPDATE_CHECK_ENTITY").executeOn(db).use {
1045+
it.moveToFirst()
1046+
Assert.assertThat(it.getString(it.getColumnIndex("RELEASE_NOTE")), CoreMatchers.`is`(releaseNote))
1047+
Assert.assertThat(it.getString(it.getColumnIndex("VERSION")), CoreMatchers.`is`(version))
1048+
Assert.assertThat(it.getString(it.getColumnIndex("URL_TO_APK")), CoreMatchers.`is`(urlApk))
1049+
Assert.assertThat(it.getString(it.getColumnIndex("APK_SHA256")), CoreMatchers.`is`(apkSha256))
1050+
Assert.assertThat(it.getString(it.getColumnIndex("URL_TO_RELEASE_NOTE")), CoreMatchers.`is`(urlReleaseNote))
1051+
}
1052+
}
9541053
}

data/src/main/java/org/cryptomator/data/db/DatabaseUpgrades.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package org.cryptomator.data.db;
22

3-
import static java.lang.String.format;
4-
53
import java.util.ArrayList;
64
import java.util.Collections;
75
import java.util.Comparator;
@@ -12,6 +10,8 @@
1210
import javax.inject.Inject;
1311
import javax.inject.Singleton;
1412

13+
import static java.lang.String.format;
14+
1515
@Singleton
1616
class DatabaseUpgrades {
1717

@@ -31,7 +31,8 @@ public DatabaseUpgrades( //
3131
Upgrade9To10 upgrade9To10, //
3232
Upgrade10To11 upgrade10To11, //
3333
Upgrade11To12 upgrade11To12, //
34-
Upgrade12To13 upgrade12To13
34+
Upgrade12To13 upgrade12To13, //
35+
Upgrade13To14 upgrade13To14
3536
) {
3637

3738
availableUpgrades = defineUpgrades( //
@@ -47,7 +48,8 @@ public DatabaseUpgrades( //
4748
upgrade9To10, //
4849
upgrade10To11, //
4950
upgrade11To12, //
50-
upgrade12To13);
51+
upgrade12To13, //
52+
upgrade13To14);
5153
}
5254

5355
private Map<Integer, List<DatabaseUpgrade>> defineUpgrades(DatabaseUpgrade... upgrades) {

0 commit comments

Comments
 (0)