Commit 2e0179ce authored by Daniel Wolf's avatar Daniel Wolf
Browse files

Merge branch 'master' of https://git.frostnerd.com/PublicAndroidApps/smokescreen into translations

parents 71ac1c62 b04d5d6d
Pipeline #4869 failed with stages
in 3 minutes and 11 seconds
......@@ -5,6 +5,7 @@ variables:
ANDROID_SDK_TOOLS: "26.1.1"
stages:
- test
- build
before_script:
......@@ -56,3 +57,14 @@ build_fdroid:
- app/build/outputs/mapping/adblocker/fdroid/mapping.txt
only:
- master
test_build_release:
stage: test
script:
- ./gradlew clean check assembleNormalRelease --stacktrace -x test
except:
- /^no_ci.*$/
- /^no_build.*$/
- /^no_build_no_tests.*$/
- /^no_build_no_deploy.*$/
- master
\ No newline at end of file
......@@ -92,9 +92,8 @@ dependencies {
implementation 'com.frostnerd.utilskt:preferences:1.5.11'
implementation 'com.frostnerd.utilskt:navigationdraweractivity:1.3.17'
implementation 'com.frostnerd.utilskt:encrypteddnstunnelproxy:1.5.110'
implementation 'com.frostnerd.utilskt:encrypteddnstunnelproxy:1.5.112'
implementation 'com.frostnerd.utilskt:general:1.0.15'
implementation 'com.frostnerd.utils:materialedittext:1.0.20'
implementation 'com.frostnerd.utils:design:1.0.17'
implementation 'com.frostnerd.utilskt:networking:1.0.3'
implementation 'com.frostnerd.utilskt:adapters:1.1.0'
......
{
"formatVersion": 1,
"database": {
"version": 7,
"identityHash": "4aa62cae2ae68bc4d2a314f44806a334",
"entities": [
{
"tableName": "CachedResponse",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`dnsName` TEXT NOT NULL, `type` INTEGER NOT NULL, `records` TEXT NOT NULL, PRIMARY KEY(`dnsName`, `type`))",
"fields": [
{
"fieldPath": "dnsName",
"columnName": "dnsName",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "records",
"columnName": "records",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"dnsName",
"type"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DnsQuery",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type` INTEGER NOT NULL, `name` TEXT NOT NULL, `askedServer` TEXT, `fromCache` INTEGER NOT NULL, `questionTime` INTEGER NOT NULL, `responseTime` INTEGER NOT NULL, `responses` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "askedServer",
"columnName": "askedServer",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "fromCache",
"columnName": "fromCache",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "questionTime",
"columnName": "questionTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "responseTime",
"columnName": "responseTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "responses",
"columnName": "responses",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "DnsRule",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `stagingType` INTEGER, `type` INTEGER NOT NULL, `host` TEXT NOT NULL, `target` TEXT NOT NULL, `ipv6Target` TEXT, `importedFrom` INTEGER, FOREIGN KEY(`importedFrom`) REFERENCES `HostSource`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "stagingType",
"columnName": "stagingType",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "host",
"columnName": "host",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "target",
"columnName": "target",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "ipv6Target",
"columnName": "ipv6Target",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "importedFrom",
"columnName": "importedFrom",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [
{
"name": "index_DnsRule_importedFrom",
"unique": false,
"columnNames": [
"importedFrom"
],
"createSql": "CREATE INDEX `index_DnsRule_importedFrom` ON `${TABLE_NAME}` (`importedFrom`)"
},
{
"name": "index_DnsRule_host_type",
"unique": false,
"columnNames": [
"host",
"type"
],
"createSql": "CREATE INDEX `index_DnsRule_host_type` ON `${TABLE_NAME}` (`host`, `type`)"
}
],
"foreignKeys": [
{
"table": "HostSource",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"importedFrom"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "HostSource",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `enabled` INTEGER NOT NULL, `name` TEXT NOT NULL, `source` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "enabled",
"columnName": "enabled",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "source",
"columnName": "source",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '4aa62cae2ae68bc4d2a314f44806a334')"
]
}
}
\ No newline at end of file
......@@ -5,7 +5,6 @@ import android.content.DialogInterface
import android.content.Intent
import android.content.res.ColorStateList
import android.graphics.Color
import android.hardware.biometrics.BiometricPrompt
import android.hardware.fingerprint.FingerprintManager
import android.os.*
import android.text.Editable
......@@ -15,16 +14,14 @@ import android.view.Window
import android.widget.EditText
import android.widget.ImageView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.widget.AppCompatImageView
import androidx.core.widget.ImageViewCompat
import com.frostnerd.lifecyclemanagement.BaseActivity
import com.frostnerd.materialedittext.MaterialEditText
import com.frostnerd.smokescreen.R
import com.frostnerd.smokescreen.canUseFingerprintAuthentication
import com.frostnerd.smokescreen.getPreferences
import com.frostnerd.smokescreen.service.Command
import com.frostnerd.smokescreen.service.DnsVpnService
import kotlinx.android.synthetic.main.dialog_pin.view.*
import java.math.BigInteger
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
......@@ -114,23 +111,22 @@ class PinActivity: BaseActivity() {
view.findViewById<ImageView>(R.id.fingerprintImage).visibility = View.GONE
}
val pinInput = view.findViewById<EditText>(R.id.pinInput)
val pinInputMet = view.findViewById<MaterialEditText>(R.id.pinInputMet)
dialog?.getButton(DialogInterface.BUTTON_POSITIVE)?.setOnClickListener {
if(pinInput.text.toString() == getPreferences().pin.toString() || hashMD5(pinInput.text.toString()) == masterPassword) {
pinInputMet.indicatorState = MaterialEditText.IndicatorState.CORRECT
view.pinInputTil.error = null
onPinPassed()
} else {
(getSystemService(Context.VIBRATOR_SERVICE) as Vibrator).vibrate(200)
pinInputMet.indicatorState = MaterialEditText.IndicatorState.INCORRECT
view.pinInputTil.error = getString(R.string.error_invalid_pin)
handler.postDelayed( {
pinInputMet.indicatorState = MaterialEditText.IndicatorState.UNDEFINED
view.pinInputTil.error = null
},2000)
}
}
pinInput.addTextChangedListener(object:TextWatcher {
override fun afterTextChanged(s: Editable?) {
pinInputMet.indicatorState = MaterialEditText.IndicatorState.UNDEFINED
view.pinInputTil.error = null
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
......
......@@ -37,7 +37,7 @@ import com.frostnerd.smokescreen.database.repository.HostSourceRepository
@Database(entities = [CachedResponse::class, DnsQuery::class, DnsRule::class, HostSource::class], version = AppDatabase.currentVersion)
abstract class AppDatabase : RoomDatabase() {
companion object {
const val currentVersion:Int = 6
const val currentVersion:Int = 7
}
abstract fun cachedResponseDao(): CachedResponseDao
......@@ -49,4 +49,16 @@ abstract class AppDatabase : RoomDatabase() {
fun dnsQueryRepository() = DnsQueryRepository(dnsQueryDao())
fun dnsRuleRepository() = DnsRuleRepository(dnsRuleDao())
fun hostSourceRepository() = HostSourceRepository(hostSourceDao())
fun recreateDnsRuleIndizes() {
this.openHelper.writableDatabase.apply {
runInTransaction {
execSQL("DROP INDEX `index_DnsRule_importedFrom`")
execSQL("DROP INDEX `index_DnsRule_host_type`")
execSQL("CREATE INDEX `index_DnsRule_importedFrom` ON `DnsRule` (`importedFrom`)")
execSQL("CREATE INDEX `index_DnsRule_host_type` ON `DnsRule` (`host`, `type`)")
}
}
}
}
\ No newline at end of file
......@@ -54,17 +54,22 @@ private val MIGRATION_5_6 = migration(5, 6) {
it.execSQL("CREATE TABLE IF NOT EXISTS `DnsRule` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `stagingType` INTEGER, `type` INTEGER NOT NULL, `host` TEXT NOT NULL, `target` TEXT NOT NULL, `ipv6Target` TEXT, `importedFrom` INTEGER, FOREIGN KEY(`importedFrom`) REFERENCES `HostSource`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )")
it.execSQL("CREATE TABLE IF NOT EXISTS `HostSource` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `enabled` INTEGER NOT NULL, `name` TEXT NOT NULL, `source` TEXT NOT NULL)")
it.execSQL("CREATE INDEX `index_DnsRule_importedFrom` ON `DnsRule` (`importedFrom`)")
it.execSQL("CREATE INDEX `index_DnsRule_host` ON `DnsRule` (`host`)")
it.execSQL("CREATE INDEX `index_DnsRule_host_type` ON `DnsRule` (`host`, `type`)")
Logger.logIfOpen("DB_MIGRATION", "Migration from 5 to 6 completed")
}
private val MIGRATION_6_7 = migration(6,7) {
Logger.logIfOpen("DB_MIGRATION", "Migrating from 6 to 7")
it.execSQL("DROP INDEX `index_DnsRule_host`")
Logger.logIfOpen("DB_MIGRATION", "Migration from 6 to 7 completed")
}
fun Context.getDatabase(): AppDatabase {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(applicationContext, AppDatabase::class.java, "data")
.allowMainThreadQueries()
.addMigrations(MIGRATION_2_X, MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6)
.addMigrations(MIGRATION_2_X, MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6, MIGRATION_6_7)
.build()
}
return INSTANCE!!
......
......@@ -30,7 +30,7 @@ import org.minidns.record.Record
childColumns = arrayOf("importedFrom"),
onDelete = ForeignKey.NO_ACTION
)],
indices = [Index("importedFrom"), Index("host"), Index("host", "type")]
indices = [Index("importedFrom"), Index("host", "type")]
)
@TypeConverters(DnsTypeConverter::class)
data class DnsRule(
......
......@@ -64,7 +64,7 @@ class NewHostSourceDialog (context: Context,
nameTil.error = null
}
if(url.text.isNullOrBlank() || !URLUtil.isValidUrl(url.text.toString())) {
urlTil.error = context.getString(R.string.dialog_newhostsource_url_invalid)
urlTil.error = context.getString(R.string.error_invalid_url)
valid = false
} else {
urlTil.error = null
......
......@@ -17,10 +17,11 @@ import com.frostnerd.encrypteddnstunnelproxy.tls.AbstractTLSDnsHandle
import com.frostnerd.encrypteddnstunnelproxy.tls.TLS
import com.frostnerd.encrypteddnstunnelproxy.tls.TLSUpstreamAddress
import com.frostnerd.lifecyclemanagement.BaseDialog
import com.frostnerd.materialedittext.MaterialEditText
import com.frostnerd.smokescreen.R
import com.frostnerd.smokescreen.getPreferences
import com.frostnerd.smokescreen.log
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import kotlinx.android.synthetic.main.dialog_new_server.*
import kotlinx.android.synthetic.main.dialog_new_server.view.*
......@@ -75,7 +76,19 @@ class NewServerDialog(
setOnShowListener {
addUrlTextWatcher(primaryServerWrap, primaryServer, false)
addUrlTextWatcher(secondaryServerWrap, secondaryServer, true)
serverName.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
serverNameWrap.error = if (s.isNullOrBlank()) context.getString(R.string.error_invalid_servername)
else null
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
})
serverNameWrap.error = context.getString(R.string.error_invalid_servername)
getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener {
if (inputsValid()) {
val name = serverName.text.toString()
......@@ -120,11 +133,15 @@ class NewServerDialog(
if (dnsOverHttps) {
if(titleOverride == null) setTitle(R.string.dialog_newserver_title_https)
view.primaryServer.setHint(R.string.dialog_newserver_primaryserver_hint)
view.secondaryServer.setHint(R.string.dialog_newserver_secondaryserver_hint)
view.secondaryServer.apply {
if(isFocused || error != null) setHint(R.string.dialog_newserver_secondaryserver_hint)
}
}else {
if(titleOverride == null) setTitle(R.string.dialog_newserver_title_tls)
view.primaryServer.setHint(R.string.dialog_newserver_primaryserver_hint_dot)
view.secondaryServer.setHint(R.string.dialog_newserver_secondaryserver_hint_dot)
view.secondaryServer.apply {
if(isFocused || error != null) setHint(R.string.dialog_newserver_secondaryserver_hint_dot)
}
}
if(titleOverride != null) setTitle(titleOverride)
}
......@@ -250,7 +267,7 @@ class NewServerDialog(
else TLSUpstreamAddress(parsedHost)
}
fun addUrlTextWatcher(materialEditText: MaterialEditText, editText: EditText, emptyAllowed: Boolean) {
fun addUrlTextWatcher(input: TextInputLayout, editText: TextInputEditText, emptyAllowed: Boolean) {
editText.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {
val valid =
......@@ -258,10 +275,9 @@ class NewServerDialog(
s.toString()
))
materialEditText.indicatorState = if (valid) {
if (s.isBlank()) MaterialEditText.IndicatorState.UNDEFINED
else MaterialEditText.IndicatorState.CORRECT
} else MaterialEditText.IndicatorState.INCORRECT
input.error = if (valid) {
null
} else context.getString(R.string.error_invalid_url)
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
......@@ -273,10 +289,9 @@ class NewServerDialog(
})
}
fun inputsValid(): Boolean = serverNameWrap.indicatorState != MaterialEditText.IndicatorState.INCORRECT &&
primaryServerWrap.indicatorState != MaterialEditText.IndicatorState.INCORRECT &&
secondaryServerWrap.indicatorState != MaterialEditText.IndicatorState.INCORRECT
fun inputsValid(): Boolean = serverNameWrap.error == null &&
primaryServerWrap.error == null &&
secondaryServerWrap.error == null
override fun destroy() {}
}
\ No newline at end of file
......@@ -49,7 +49,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
private var werePreferencesAdded = false
private var preferenceListener: SharedPreferences.OnSharedPreferenceChangeListener =
SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
requireContext().getPreferences().notifyPreferenceChangedFromExternal(key)
context?.getPreferences()?.notifyPreferenceChangedFromExternal(key)
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
......
......@@ -203,6 +203,7 @@ class RuleImportService : Service() {
updateNotificationFinishing()
dnsRuleDao.deleteMarkedRules()
dnsRuleDao.commitStaging()
getDatabase().recreateDnsRuleIndizes()
showSuccessNotification()
}
log("All imports finished.")
......
......@@ -13,15 +13,15 @@ import android.widget.EditText
import com.frostnerd.dnstunnelproxy.DnsServerInformation
import com.frostnerd.encrypteddnstunnelproxy.HttpsDnsServerInformation
import com.frostnerd.lifecyclemanagement.BaseActivity
import com.frostnerd.materialedittext.MaterialEditText
import com.frostnerd.smokescreen.R
import com.frostnerd.smokescreen.activity.BackgroundVpnConfigureActivity
import com.frostnerd.smokescreen.dialog.NewServerDialog
import com.frostnerd.smokescreen.fromServerUrls
import com.frostnerd.smokescreen.hasHttpsServer
import com.frostnerd.smokescreen.tlsServerFromHosts
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import kotlinx.android.synthetic.main.activity_tasker_configure.*
import kotlinx.android.synthetic.main.dialog_new_server.view.*
/*
* Copyright (C) 2019 Daniel Wolf (Ch4t4r)
......@@ -104,6 +104,7 @@ class ConfigureActivity : BaseActivity() {
}
}
}
setHints()
}
}
}
......@@ -155,28 +156,35 @@ class ConfigureActivity : BaseActivity() {
validationRegex = if (position == 0) NewServerDialog.SERVER_URL_REGEX else NewServerDialog.TLS_REGEX
primaryServer.text = primaryServer.text
secondaryServer.text = secondaryServer.text
if(position == 1) {
primaryServer.setHint(R.string.dialog_newserver_primaryserver_hint)
secondaryServer.setHint(R.string.dialog_newserver_secondaryserver_hint)
} else {
primaryServer.setHint(R.string.dialog_newserver_primaryserver_hint_dot)
secondaryServer.setHint(R.string.dialog_newserver_secondaryserver_hint_dot)
}
setHints()
}
}
addUrlTextWatcher(primaryServerWrap, primaryServer, false)
addUrlTextWatcher(secondaryServerWrap, secondaryServer, true)
setHints()
}
private fun addUrlTextWatcher(materialEditText: MaterialEditText, editText: EditText, emptyAllowed: Boolean) {
private fun setHints() {
val doh = serverType.selectedItemPosition == 0
println("DOH: $doh")
primaryServer.hint = if(primaryServer.hasFocus() || primaryServerWrap.error != null) {
if(doh) getString(R.string.dialog_newserver_primaryserver_hint)
else getString(R.string.dialog_newserver_primaryserver_hint_dot)
} else null
secondaryServer.hint = if(secondaryServer.hasFocus()) {
if(doh) getString(R.string.dialog_newserver_secondaryserver_hint)
else getString(R.string.dialog_newserver_secondaryserver_hint_dot)
} else null
}
private fun addUrlTextWatcher(input: TextInputLayout, editText: TextInputEditText, emptyAllowed: Boolean) {
editText.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {
val valid = (emptyAllowed && s.isBlank()) || validationRegex.matches(s.toString())
materialEditText.indicatorState = if (valid) {
if (s.isBlank()) MaterialEditText.IndicatorState.UNDEFINED
else MaterialEditText.IndicatorState.CORRECT
} else MaterialEditText.IndicatorState.INCORRECT
input.error = if (valid) {
null
} else getString(R.string.error_invalid_url)
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
......@@ -186,6 +194,9 @@ class ConfigureActivity : BaseActivity() {