Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
3e6a0f2
Update to SQLDelight version of libpretixsync
antweb Jul 23, 2024
8caf23e
Set up SCAN-specific SQLDelight queries
antweb Jan 22, 2025
4bf9b13
Remove unused query
antweb Jan 22, 2025
577587a
Migrate MainActivity
antweb Jan 22, 2025
cbadca8
Migrate PrintUtils
antweb Jan 22, 2025
96947e1
Migrate CheckInListSelectActivity
antweb Feb 5, 2025
93ed873
Migrate EventConfigActivity
antweb Feb 5, 2025
209bc1e
Update to latest SQLDelight version of libpretixsync
antweb Feb 5, 2025
132112f
Migrate Settings.kt
antweb Feb 5, 2025
6fdec96
Remove requery BlockingEntityStore
antweb Feb 12, 2025
684d9ac
Fix question handling
antweb Feb 19, 2025
bd7b090
Remove manual version check and schema creation
antweb Feb 26, 2025
fffc9da
Re-open database after SQLCipher migration
antweb Mar 26, 2025
3ae5d65
Fail migrations if database is too old
antweb Apr 2, 2025
53f76b3
Use new database name constant
antweb Apr 16, 2025
d2def85
Remove unused SQLCipher classes
antweb Apr 16, 2025
edc75ef
Remove requery from project
antweb Apr 23, 2025
7327ea4
Use Question data from CheckResult
antweb Apr 23, 2025
e83738f
Fix attendee naming handling
antweb Apr 23, 2025
d1d2718
Update to latest libpretixui
antweb May 7, 2025
662063f
Bump to 3.0.0-alpha1
robbi5 Jul 7, 2025
d8b1455
Update libpretixsync
robbi5 Jul 7, 2025
d0cb82f
Bump to 3.0.0-alpha2
robbi5 Jul 7, 2025
e0a106c
Update libpretixsync
robbi5 Jul 15, 2025
bc6364c
Bump to 3.0.0-alpha3
robbi5 Jul 15, 2025
1a43532
Update libpretixsync
robbi5 Jul 15, 2025
b412bba
Bump to 3.0.0-alpha4
robbi5 Jul 15, 2025
c0b4e4b
Add screen, so upgrades from 1.11.4 (51) don't crash but instruct the…
robbi5 Jul 15, 2025
6001921
Bump to 3.0.0-alpha5
robbi5 Jul 15, 2025
ed28589
Fix missed panic call in onCreate/onResume
robbi5 Jul 15, 2025
8931f5e
Bump to 3.0.0-alpha6
robbi5 Jul 15, 2025
884c386
Panic should report to sentry too
robbi5 Jul 15, 2025
a48379b
Update to latest libpretixsync and libpretixui
antweb Jul 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions pretixscan/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ plugins {
id "com.android.application"
id "org.jetbrains.kotlin.android"
id "org.jetbrains.kotlin.kapt"
id "app.cash.sqldelight"
}

def flavors = [:]
Expand All @@ -23,8 +24,8 @@ android {
applicationId "eu.pretix.pretixscan.droid"
minSdkVersion 21
targetSdkVersion 34
versionCode 99
versionName "2.10.3"
versionCode 100
versionName "3.0.0-alpha6"
vectorDrawables.useSupportLibrary = true
vectorDrawables.generatedDensities = ['ldpi', 'mdpi', 'hdpi', 'xhdpi', 'xxhdpi']
multiDexEnabled true
Expand Down Expand Up @@ -151,16 +152,14 @@ dependencies {
implementation 'io.sentry:sentry-android:7.14.0'
implementation 'org.slf4j:slf4j-nop:1.7.30'
implementation "joda-time:joda-time:$joda_version"
implementation "io.requery:requery:$requery_version"
implementation "io.requery:requery-android:$requery_version"
implementation "io.requery:requery-kotlin:$requery_version"
implementation 'net.zetetic:sqlcipher-android:4.5.6'
implementation "net.sourceforge.streamsupport:streamsupport-cfuture:$cfuture_version"
implementation "com.fasterxml.jackson.core:jackson-databind:$jackson_core_version"
implementation "com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_kotlin_version"
implementation "com.fasterxml.jackson.datatype:jackson-datatype-joda:$jackson_kotlin_version"
implementation "net.i2p.crypto:eddsa:$eddsa_version"
implementation "com.google.protobuf:protobuf-javalite:$protobuf_version"
implementation "app.cash.sqldelight:android-driver:$sqldelight_version"

implementation 'com.facebook.soloader:soloader:0.10.5'
debugImplementation 'com.facebook.flipper:flipper:0.261.0'
Expand All @@ -176,10 +175,24 @@ dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'

// libpretixsync
kapt 'io.requery:requery-processor:1.6.0'
implementation(project(':libpretixsync')) {
transitive = false
}
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.github.apg-mobile:android-round-textview:v1.0.0'
}

sqldelight {
databases {
SyncDatabase {
packageName = "eu.pretix.pretixscan.sqldelight"

dependency(project(":libpretixsync"))

// Oldest dialect supported by SQLDelight 2.0.2
// In Android projects, it will auto-select based on SDK
// but not go lower than 3.18 (Android 9 is still on 3.8)
dialect "app.cash.sqldelight:sqlite-3-18-dialect:$sqldelight_version"
}
}
}
5 changes: 5 additions & 0 deletions pretixscan/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@
android:name=".ui.CheckInListSelectActivity"
android:label="@string/operation_select_event"
android:theme="@style/AppTheme.Dialog.Animated" />
<activity
android:name=".ui.PleaseReinstallActivity"
android:label="@string/headline_please_reinstall"
android:exported="false"
android:theme="@style/AppTheme.NoActionBar" />

<provider
android:name="androidx.core.content.FileProvider"
Expand Down

This file was deleted.

This file was deleted.

172 changes: 99 additions & 73 deletions pretixscan/app/src/main/java/eu/pretix/pretixscan/droid/PretixScan.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,107 +4,133 @@ import android.database.sqlite.SQLiteException
import android.os.Build
import androidx.appcompat.app.AppCompatDelegate
import androidx.multidex.MultiDexApplication
import app.cash.sqldelight.db.QueryResult
import app.cash.sqldelight.driver.android.AndroidSqliteDriver
import com.facebook.flipper.android.AndroidFlipperClient
import com.facebook.flipper.core.FlipperClient
import com.facebook.soloader.SoLoader
import eu.pretix.libpretixsync.Models
import eu.pretix.libpretixsync.check.AsyncCheckProvider
import eu.pretix.libpretixsync.check.OnlineCheckProvider
import eu.pretix.libpretixsync.check.ProxyCheckProvider
import eu.pretix.libpretixsync.check.TicketCheckProvider
import eu.pretix.libpretixsync.db.Migrations
import eu.pretix.libpretixsync.sqldelight.AndroidUtilDateAdapter
import eu.pretix.libpretixsync.sqldelight.BigDecimalAdapter
import eu.pretix.libpretixsync.sqldelight.Migrations
import eu.pretix.pretixscan.droid.connectivity.ConnectivityHelper
import eu.pretix.pretixscan.sqldelight.SyncDatabase
import eu.pretix.pretixscan.utils.KeystoreHelper
import io.requery.BlockingEntityStore
import io.requery.Persistable
import io.requery.sql.EntityDataStore
import eu.pretix.pretixscan.utils.createDriver
import eu.pretix.pretixscan.utils.createSyncDatabase
import net.zetetic.database.sqlcipher.SQLiteConnection
import net.zetetic.database.sqlcipher.SQLiteDatabase
import net.zetetic.database.sqlcipher.SQLiteDatabaseHook
import java.util.concurrent.locks.ReentrantLock


class PretixScan : MultiDexApplication() {
private var dataStore: BlockingEntityStore<Persistable>? = null
val fileStorage = AndroidFileStorage(this)
val syncLock = ReentrantLock()
var flipperInit: FlipperInitializer.IntializationResult? = null
lateinit var connectivityHelper: ConnectivityHelper

private fun migrateSqlCipher(name: String, dbPass: String) {
private fun migrateSqlCipher(name: String, dbPass: String, driver: AndroidSqliteDriver): Boolean {
System.loadLibrary("sqlcipher")

val databaseFile = getDatabasePath(name)
SQLiteDatabase.openOrCreateDatabase(databaseFile, dbPass, null, null, object: SQLiteDatabaseHook {
override fun preKey(connection: SQLiteConnection) {
}
try {
driver.executeQuery(
identifier = null,
sql = "select count(*) from sqlite_master;",
mapper = { cursor ->
cursor.next()
QueryResult.Value(cursor.getLong(0))
},
parameters = 0,
)
return false
} catch (e: SQLiteException) {
try {
val databaseFile = getDatabasePath(name)
SQLiteDatabase.openOrCreateDatabase(
databaseFile,
dbPass,
null,
null,
object : SQLiteDatabaseHook {
override fun preKey(connection: SQLiteConnection) {
}

override fun postKey(connection: SQLiteConnection) {
val result = connection.executeForLong("PRAGMA cipher_migrate;", emptyArray(), null)
if (result != 0L) {
throw SQLiteException("cipher_migrate failed")
}
override fun postKey(connection: SQLiteConnection) {
val result = connection.executeForLong(
"PRAGMA cipher_migrate;",
emptyArray(),
null
)
if (result != 0L) {
throw SQLiteException("cipher_migrate failed")
}
}
}).close()
return true
} catch (e: SQLiteException) {
// still not decrypted? then we probably lost the key due to a keystore issue
// let's start fresh, there's no reasonable other way to let the user out of this
this.deleteDatabase(name)
return true
}
}).close()
}
}

val data: BlockingEntityStore<Persistable>
get() {
if (dataStore == null) {
if (BuildConfig.DEBUG) {
// Do not encrypt on debug, because it breaks Stetho
val source = AndroidDatabaseSource(this, Models.DEFAULT, Migrations.CURRENT_VERSION)
source.setLoggingEnabled(BuildConfig.DEBUG)
val configuration = source.configuration
dataStore = EntityDataStore(configuration)
} else {
val dbPass = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) KeystoreHelper.secureValue(KEYSTORE_PASSWORD, true)
else KEYSTORE_PASSWORD

var source = AndroidSqlCipherDatabaseSource(
this,
Models.DEFAULT,
Models.DEFAULT.name,
dbPass,
Migrations.CURRENT_VERSION
)
try {
// check if database has been decrypted
source.readableDatabase.rawQuery("select count(*) from sqlite_master;", emptyArray())
} catch (e: SQLiteException) {
try {
source.close()
migrateSqlCipher(Models.DEFAULT.name, dbPass)
source = AndroidSqlCipherDatabaseSource(
this,
Models.DEFAULT,
Models.DEFAULT.name,
dbPass,
Migrations.CURRENT_VERSION
)
source.readableDatabase.rawQuery("select count(*) from sqlite_master;", emptyArray())
} catch (e: SQLiteException) {
// still not decrypted? then we probably lost the key due to a keystore issue
// let's start fresh, there's no reasonable other way to let the user out of this
this.deleteDatabase(Models.DEFAULT.getName())
source = AndroidSqlCipherDatabaseSource(
this,
Models.DEFAULT,
Models.DEFAULT.name,
dbPass,
Migrations.CURRENT_VERSION
)
}
}
source.setLoggingEnabled(false)
val db: SyncDatabase by lazy {
val dbName = Migrations.DEFAULT_DATABASE_NAME

val configuration = source.configuration
dataStore = EntityDataStore(configuration)
}
val dbPass = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) KeystoreHelper.secureValue(KEYSTORE_PASSWORD, true)
else KEYSTORE_PASSWORD

val androidDriver = if (BuildConfig.DEBUG) {
createDriver(
context = this.applicationContext,
dbName = dbName,
dbPass = null,
)
} else {
System.loadLibrary("sqlcipher")
val driver = createDriver(
context = this.applicationContext,
dbName = dbName,
dbPass = dbPass,
)

val reopen = migrateSqlCipher(dbName, dbPass, driver)
if (reopen) {
// Re-open database if we had to delete it during migration or the cipher had to be migrated
driver.close()
createDriver(
context = this.applicationContext,
dbName = dbName,
dbPass = dbPass,
)
} else {
driver
}
return dataStore!!
}

// Uncomment LogSqliteDriver for verbose logging
val driver = if(BuildConfig.DEBUG) {
// LogSqliteDriver(androidDriver) {
// Log.d("SQLDelight", it)
// }
androidDriver
} else {
androidDriver
}

createSyncDatabase(
driver = driver,
dateAdapter = AndroidUtilDateAdapter(),
bigDecimalAdapter = BigDecimalAdapter(),
)
}

override fun onCreate() {
super.onCreate()

Expand All @@ -123,7 +149,7 @@ class PretixScan : MultiDexApplication() {
if (conf.proxyMode) {
return ProxyCheckProvider(conf, AndroidHttpClientFactory(this))
} else if (conf.offlineMode) {
return AsyncCheckProvider(conf, data)
return AsyncCheckProvider(conf, db)
} else {
var fallback: TicketCheckProvider? = null
var fallbackTimeout = 30000
Expand All @@ -138,9 +164,9 @@ class PretixScan : MultiDexApplication() {
"20s" -> 20000
else -> throw Exception("Unknown offline mode ")
}
fallback = AsyncCheckProvider(conf, data)
fallback = AsyncCheckProvider(conf, db)
}
return OnlineCheckProvider(conf, AndroidHttpClientFactory(this), data, fileStorage, fallback, fallbackTimeout)
return OnlineCheckProvider(conf, AndroidHttpClientFactory(this), db, fileStorage, fallback, fallbackTimeout)
}
}

Expand Down
Loading