Commit 266b1011 authored by Daniel Wolf's avatar Daniel Wolf
Browse files

Now using Room for database persistance

parent 21b1dbd6
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 28
......@@ -34,6 +35,8 @@ android {
}
dependencies {
def room_version = "2.1.0-alpha03"
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
......@@ -44,12 +47,16 @@ dependencies {
implementation 'com.frostnerd.utils:materialedittext:1.0.17'
implementation 'com.frostnerd.utils:design:1.0.17'
implementation 'com.frostnerd.utilskt:networking:1.0.0'
implementation 'com.frostnerd.utils:database:1.1.16'
implementation 'com.frostnerd.utilskt:adapters:1.0.1'
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation "androidx.preference:preference:1.0.0"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-coroutines:$room_version"
testImplementation "androidx.room:room-testing:$room_version"
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0-alpha'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha2'
......
package com.frostnerd.smokescreen.activity
import android.content.ContentResolver
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.os.PersistableBundle
import android.util.JsonReader
import android.util.JsonToken
import android.view.Window
import androidx.appcompat.app.AlertDialog
import com.frostnerd.lifecyclemanagement.BaseActivity
import com.frostnerd.smokescreen.R
import com.frostnerd.smokescreen.database.DatabaseHelper
import com.frostnerd.smokescreen.database.entities.UserServerConfiguration
import com.frostnerd.smokescreen.database.getDatabase
import com.frostnerd.smokescreen.getPreferences
import com.frostnerd.smokescreen.showInfoTextDialog
import java.io.InputStreamReader
import java.lang.Exception
import java.net.URL
......@@ -114,8 +111,12 @@ class ServerImportActivity : BaseActivity() {
}
.setPositiveButton(R.string.all_yes) { dialog, _ ->
dialog.cancel()
val configToSave = UserServerConfiguration(config.name!!, config.primaryUrl!!, config.secondaryUrl)
DatabaseHelper.getInstance(this@ServerImportActivity).insert(configToSave)
val configToSave = UserServerConfiguration(
name = config.name!!,
primaryServerUrl = config.primaryUrl!!,
secondaryServerUrl = config.secondaryUrl
)
getDatabase().userServerConfigurationDao().insert(configToSave)
val intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
......
package com.frostnerd.smokescreen.database
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import com.frostnerd.database.orm.Entity
import androidx.room.Database
import androidx.room.RoomDatabase
import com.frostnerd.smokescreen.database.dao.CachedResponseDao
import com.frostnerd.smokescreen.database.dao.UserServerConfigurationDao
import com.frostnerd.smokescreen.database.entities.CachedResponse
import com.frostnerd.smokescreen.database.entities.UserServerConfiguration
......@@ -16,38 +17,8 @@ import com.frostnerd.smokescreen.database.entities.UserServerConfiguration
* development@frostnerd.com
*/
class DatabaseHelper private constructor(context: Context) :
com.frostnerd.database.DatabaseHelper(context, NAME, VERSION, ENTITIES) {
companion object {
val NAME = "data"
val VERSION = 2
val ENTITIES: Set<Class<out Entity>> = setOf(UserServerConfiguration::class.java, CachedResponse::class.java)
private var INSTANCE: DatabaseHelper? = null
fun getInstance(context: Context): DatabaseHelper {
if (INSTANCE == null) {
INSTANCE = DatabaseHelper(context)
}
return INSTANCE!!
}
}
override fun onBeforeUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
if(oldVersion < 2) {
getSQLHandler(CachedResponse::class.java).createTable(db)
}
}
override fun onAfterUpgrade(p0: SQLiteDatabase?, p1: Int, p2: Int) {
}
override fun onAfterCreate(p0: SQLiteDatabase?) {
}
override fun onBeforeCreate(p0: SQLiteDatabase?) {
}
}
fun Context.getDatabase(): DatabaseHelper {
return DatabaseHelper.getInstance(this)
@Database(entities = [UserServerConfiguration::class, CachedResponse::class], version = 3)
abstract class AppDatabase : RoomDatabase() {
abstract fun userServerConfigurationDao(): UserServerConfigurationDao
abstract fun cachedResponseDao(): CachedResponseDao
}
\ No newline at end of file
package com.frostnerd.smokescreen.database
import android.content.Context
import androidx.room.Room
/**
* Copyright Daniel Wolf 2018
* All rights reserved.
* Code may NOT be used without proper permission, neither in binary nor in source form.
* All redistributions of this software in source code must retain this copyright header
* All redistributions of this software in binary form must visibly inform users about usage of this software
*
* development@frostnerd.com
*/
private var INSTANCE:AppDatabase? = null
fun Context.getDatabase():AppDatabase {
if(INSTANCE == null) {
INSTANCE = Room.databaseBuilder(applicationContext, AppDatabase::class.java, "data").allowMainThreadQueries().build()
}
return INSTANCE!!
}
\ No newline at end of file
package com.frostnerd.smokescreen.database.serializers;
package com.frostnerd.smokescreen.database.converters
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.frostnerd.database.orm.Serializer;
import androidx.room.TypeConverter
import com.google.gson.Gson
import com.google.gson.JsonElement
import com.google.gson.reflect.TypeToken
/**
* Copyright Daniel Wolf 2018
......@@ -10,23 +11,17 @@ import com.frostnerd.database.orm.Serializer;
* Code may NOT be used without proper permission, neither in binary nor in source form.
* All redistributions of this software in source code must retain this copyright header
* All redistributions of this software in binary form must visibly inform users about usage of this software
* <p>
*
* development@frostnerd.com
*/
public class LongSerializer extends Serializer<Long> {
@Override
protected String serializeValue(@NonNull Long aLong) {
return aLong.toString();
class DnsRecordMapConverter {
@TypeConverter
fun stringToMap(value: String): Map<String, Long> {
return Gson().fromJson(value, object : TypeToken<Map<String, Long>>() {}.type)
}
@Nullable
@Override
public Long deserialize(@NonNull String s) {
return Long.parseLong(s);
@TypeConverter
fun mapToString(value: Map<String, Long>?): String {
return if (value == null) "" else Gson().toJson(value)
}
@Override
public String serializeNull() {
return "";
}
}
}
\ No newline at end of file
package com.frostnerd.smokescreen.database.converters
import androidx.room.TypeConverter
import org.minidns.record.Record
/**
* Copyright Daniel Wolf 2018
* All rights reserved.
* Code may NOT be used without proper permission, neither in binary nor in source form.
* All redistributions of this software in source code must retain this copyright header
* All redistributions of this software in binary form must visibly inform users about usage of this software
*
* development@frostnerd.com
*/
class DnsTypeConverter {
@TypeConverter
fun fromId(value: Int): Record.TYPE {
return value.let { Record.TYPE.getType(it) }
}
@TypeConverter
fun toId(type:Record.TYPE):Int {
return type.value
}
}
\ No newline at end of file
package com.frostnerd.smokescreen.database.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import com.frostnerd.smokescreen.database.entities.CachedResponse
import com.frostnerd.smokescreen.database.entities.UserServerConfiguration
/**
* Copyright Daniel Wolf 2018
* All rights reserved.
* Code may NOT be used without proper permission, neither in binary nor in source form.
* All redistributions of this software in source code must retain this copyright header
* All redistributions of this software in binary form must visibly inform users about usage of this software
*
* development@frostnerd.com
*/
@Dao
interface CachedResponseDao {
@Query("SELECT * FROM CachedResponse")
fun getAll(): List<CachedResponse>
@Insert
fun insertAll(vararg cachedResponses: CachedResponse)
@Insert
fun insert(cachedResponse: CachedResponse)
@Delete
fun delete(cachedResponse: CachedResponse)
@Query("DELETE FROM CachedResponse")
fun deleteAll()
}
\ No newline at end of file
package com.frostnerd.smokescreen.database.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import com.frostnerd.smokescreen.database.entities.UserServerConfiguration
/**
* Copyright Daniel Wolf 2018
* All rights reserved.
* Code may NOT be used without proper permission, neither in binary nor in source form.
* All redistributions of this software in source code must retain this copyright header
* All redistributions of this software in binary form must visibly inform users about usage of this software
*
* development@frostnerd.com
*/
@Dao
interface UserServerConfigurationDao {
@Query("SELECT * FROM UserServerConfiguration")
fun getAll(): List<UserServerConfiguration>
@Insert
fun insertAll(vararg configurations: UserServerConfiguration)
@Insert
fun insert(configuration: UserServerConfiguration)
@Delete
fun delete(user: UserServerConfiguration)
}
\ No newline at end of file
package com.frostnerd.smokescreen.database.entities
import com.frostnerd.database.orm.Entity
import com.frostnerd.database.orm.annotations.Named
import com.frostnerd.database.orm.annotations.RowID
import com.frostnerd.database.orm.annotations.Table
import com.frostnerd.database.orm.annotations.ValueSerializer
import com.frostnerd.smokescreen.database.serializers.LongSerializer
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.TypeConverters
import com.frostnerd.smokescreen.database.converters.DnsRecordMapConverter
import com.frostnerd.smokescreen.database.converters.DnsTypeConverter
import org.minidns.record.Record
/**
* Copyright Daniel Wolf 2018
......@@ -16,18 +16,10 @@ import com.frostnerd.smokescreen.database.serializers.LongSerializer
*
* development@frostnerd.com
*/
@Table(name = "CachedResponse")
class CachedResponse : Entity() {
@RowID
var rowid:Long = -1
@Named(name="dnsName")
lateinit var dnsName:String
@Named(name="type")
var type:Int = -99
@Named(name="records")
@ValueSerializer(usedSerializer = LongSerializer::class)
var records:MutableMap<String, Long> = mutableMapOf()
}
\ No newline at end of file
@Entity(tableName = "CachedResponse", primaryKeys = ["dnsName", "type"])
@TypeConverters(DnsTypeConverter::class, DnsRecordMapConverter::class)
data class CachedResponse(
@ColumnInfo(name = "dnsName") var dnsName: String,
@ColumnInfo(name = "type") var type: Record.TYPE,
@ColumnInfo(name = "records") var records: MutableMap<String, Long>
)
\ No newline at end of file
package com.frostnerd.smokescreen.database.entities
import com.frostnerd.database.orm.Entity
import com.frostnerd.database.orm.annotations.Named
import com.frostnerd.database.orm.annotations.RowID
import com.frostnerd.database.orm.annotations.Table
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.frostnerd.encrypteddnstunnelproxy.ServerConfiguration
import com.frostnerd.encrypteddnstunnelproxy.createSimpleServerConfig
......@@ -16,26 +15,19 @@ import com.frostnerd.encrypteddnstunnelproxy.createSimpleServerConfig
*
* development@frostnerd.com
*/
@Table(name = "UserServerConfiguration")
class UserServerConfiguration():Entity() {
@RowID
var rowid:Long = -1
@Named(name="name")
lateinit var name:String
@Named(name="primaryServerUrl")
lateinit var primaryServerUrl:String
@Named(name="secondaryServerUrl")
var secondaryServerUrl:String? = null
constructor(name:String, primaryServerUrl:String, secondaryServerUrl:String?):this() {
this.name = name
this.primaryServerUrl = primaryServerUrl
this.secondaryServerUrl = secondaryServerUrl
}
@Entity(tableName = "UserServerConfiguration")
data class UserServerConfiguration(
@PrimaryKey(autoGenerate = true) var id: Int = -1,
@ColumnInfo(name = "name") var name: String,
@ColumnInfo(name = "primaryServerUrl") var primaryServerUrl: String,
@ColumnInfo(name = "secondaryServerUrl") var secondaryServerUrl: String? = null
) {
fun createPrimaryServerConfiguration(): ServerConfiguration =
ServerConfiguration.createSimpleServerConfig(primaryServerUrl)
fun createPrimaryServerConfiguration():ServerConfiguration = ServerConfiguration.createSimpleServerConfig(primaryServerUrl)
fun createSecondaryServerConfiguration():ServerConfiguration? {
return if(secondaryServerUrl != null) {
fun createSecondaryServerConfiguration(): ServerConfiguration? {
return if (secondaryServerUrl != null) {
ServerConfiguration.createSimpleServerConfig(secondaryServerUrl!!)
} else null
}
......
......@@ -54,8 +54,8 @@ class ServerChoosalDialog(context: Context, onEntrySelected: (primaryServer:Serv
addServer.setOnClickListener {
NewServerDialog(context) { name, primaryServer, secondaryServer:String? ->
val userServerConfiguration = UserServerConfiguration(name, primaryServer, secondaryServer)
context.getDatabase().insert(userServerConfiguration)
val userServerConfiguration = UserServerConfiguration(name=name, primaryServerUrl = primaryServer, secondaryServerUrl = secondaryServer)
context.getDatabase().userServerConfigurationDao().insert(userServerConfiguration)
knownServersGroup.addView(createButtonForUserConfiguration(userServerConfiguration))
}.show()
}
......@@ -94,7 +94,7 @@ class ServerChoosalDialog(context: Context, onEntrySelected: (primaryServer:Serv
buttons.add(0, createButtonForKnownConfiguration(serverInfo.name, serverInfo))
}
}
context.getDatabase().getAll(UserServerConfiguration::class.java).forEach {
context.getDatabase().userServerConfigurationDao().getAll().forEach {
buttons.add(createButtonForUserConfiguration(it))
}
}
......@@ -148,7 +148,7 @@ class ServerChoosalDialog(context: Context, onEntrySelected: (primaryServer:Serv
.setMessage(context.getString(R.string.dialog_deleteconfig_text, userConfiguration.name))
.setNegativeButton(R.string.all_no) { _, _ -> }
.setPositiveButton(R.string.all_yes) { _, _ ->
context.getDatabase().delete(userConfiguration)
context.getDatabase().userServerConfigurationDao().delete(userConfiguration)
if(button.isChecked) {
context.getPreferences().primaryServerConfig = AbstractHttpsDNSHandle.KNOWN_DNS_SERVERS[0]!!.serverConfigurations.values.first()
......
......@@ -211,7 +211,6 @@ class SettingsFragment : PreferenceFragmentCompat() {
),
blackList = requireContext().getPreferences().isBypassBlacklist
) { selected, isBlacklist ->
println("BLACKLIST: $isBlacklist")
requireContext().getPreferences().isBypassBlacklist = isBlacklist
if (selected.size != requireContext().getPreferences().userBypassPackages.size) {
log("Updated the list of user bypass packages to $selected")
......
......@@ -17,7 +17,6 @@ import android.os.Looper
import android.util.Base64
import androidx.core.app.NotificationCompat
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.frostnerd.database.orm.statementoptions.queryoptions.WhereCondition
import com.frostnerd.dnstunnelproxy.*
import com.frostnerd.encrypteddnstunnelproxy.AbstractHttpsDNSHandle
import com.frostnerd.encrypteddnstunnelproxy.HttpsDnsServerInformation
......@@ -629,7 +628,8 @@ class DnsVpnService : VpnService(), Runnable {
log("Restoring old cache")
var restored = 0
var tooOld = 0
for (cachedResponse in getDatabase().select(CachedResponse::class.java)) {
val dao = getDatabase().cachedResponseDao()
for (cachedResponse in dao.getAll()) {
val records = mutableMapOf<Record<*>, Long>()
for (record in cachedResponse.records) {
if(record.value > System.currentTimeMillis()) {
......@@ -639,10 +639,10 @@ class DnsVpnService : VpnService(), Runnable {
restored++
} else tooOld++
}
dnsCache.addToCache(DnsName.from(cachedResponse.dnsName), Record.TYPE.getType(cachedResponse.type), records)
dnsCache.addToCache(DnsName.from(cachedResponse.dnsName), cachedResponse.type, records)
}
log("$restored old records restored, deleting persisted cache. $tooOld records were too old.")
getDatabase().deleteAll(CachedResponse::class.java)
dao.deleteAll()
log("Persisted cache deleted.")
}
return dnsCache
......@@ -653,26 +653,16 @@ class DnsVpnService : VpnService(), Runnable {
type: Record.TYPE,
recordsToPersist: MutableMap<Record<*>, Long>
) {
val database = getDatabase()
val entities = database.select(
CachedResponse::class.java,
WhereCondition.equal("type", type.value.toString()),
WhereCondition.equal("dnsName", dnsName)
val dao = getDatabase().cachedResponseDao()
val entity = CachedResponse(
dnsName,
type,
mutableMapOf()
)
val entity:CachedResponse
if(!entities.isEmpty()) {
entity = entities[0]
} else {
entity = CachedResponse()
entity.dnsName = dnsName
entity.type = type.value
}
for (record in recordsToPersist) {
entity.records[Base64.encodeToString(record.key.toByteArray(), Base64.NO_WRAP)] = record.value
}
if(!entities.isEmpty()) database.update(entity)
else database.insert(entity)
dao.insert(entity)
}
private fun isPackageInstalled(packageName: String): Boolean {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment