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

Merge branch 'master' into 'translations'

Master

See merge request PublicAndroidApps/smokescreen!40
parents 697d5591 4fa62d5e
Pipeline #5116 passed with stages
in 1 minute and 7 seconds
......@@ -25,7 +25,7 @@ android {
applicationId "com.frostnerd.smokescreen"
minSdkVersion 21
targetSdkVersion 28
versionCode 43
versionCode 44
versionName "1.0-Beta"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
......@@ -74,7 +74,7 @@ android {
adblocker {
dimension "version"
versionNameSuffix "-adblock"
versionCode 43
versionCode 44
}
normal {
dimension "version"
......@@ -106,12 +106,12 @@ dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0-M2'
implementation 'com.frostnerd.utilskt:preferences:1.5.14' // https://git.frostnerd.com/AndroidUtils/preferenceskt (Accessible after logging in [free of charge])
implementation 'com.frostnerd.utilskt:navigationdraweractivity:1.3.19' // https://git.frostnerd.com/AndroidUtils/navigationdraweractivity (Accessible after logging in [free of charge])
implementation 'com.frostnerd.utilskt:encrypteddnstunnelproxy:1.5.136' // https://git.frostnerd.com/AndroidUtils/encrypteddnstunnelproxy
implementation 'com.frostnerd.utilskt:navigationdraweractivity:1.3.20' // https://git.frostnerd.com/AndroidUtils/navigationdraweractivity (Accessible after logging in [free of charge])
implementation 'com.frostnerd.utilskt:encrypteddnstunnelproxy:1.5.138' // https://git.frostnerd.com/AndroidUtils/encrypteddnstunnelproxy
implementation 'com.frostnerd.utilskt:general:1.0.16' // https://git.frostnerd.com/AndroidUtils/generalkt (Accessible after logging in [free of charge])
implementation 'com.frostnerd.utilskt:adapters:1.1.1' // https://git.frostnerd.com/AndroidUtils/Adapters (Accessible after logging in [free of charge])
implementation 'androidx.work:work-runtime:2.2.0-rc01'
implementation 'androidx.work:work-runtime:2.2.0'
implementation 'androidx.appcompat:appcompat:1.1.0-rc01'
implementation "androidx.preference:preference:1.1.0-rc01"
implementation "com.google.android.material:material:1.1.0-alpha09"
......@@ -122,7 +122,7 @@ dependencies {
implementation "androidx.room:room-coroutines:2.1.0-alpha04"
testImplementation "androidx.room:room-testing:$room_version"
implementation 'io.sentry:sentry-android:1.7.23'
implementation 'io.sentry:sentry-android:1.7.26'
implementation 'com.github.anrwatchdog:anrwatchdog:1.4.0'
implementation 'com.github.jorgecastilloprz:fabprogresscircle:1.01@aar'
......
......@@ -28,7 +28,6 @@
}
-assumenosideeffects class com.frostnerd.vpntunnelproxy.Logger {
public final void fine(...);
public final void finer(...);
public final void finest(...);
}
......
{
"formatVersion": 1,
"database": {
"version": 11,
"identityHash": "6593a50157ce88fb018043b5c151353a",
"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, `responseSource` TEXT 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": "responseSource",
"columnName": "responseSource",
"affinity": "TEXT",
"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 NOT NULL, `type` INTEGER NOT NULL, `host` TEXT NOT NULL, `target` TEXT NOT NULL, `ipv6Target` TEXT, `importedFrom` INTEGER, `isWildcard` INTEGER NOT NULL, 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": true
},
{
"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
},
{
"fieldPath": "isWildcard",
"columnName": "isWildcard",
"affinity": "INTEGER",
"notNull": true
}
],
"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_stagingType",
"unique": true,
"columnNames": [
"host",
"type",
"stagingType"
],
"createSql": "CREATE UNIQUE INDEX `index_DnsRule_host_type_stagingType` ON `${TABLE_NAME}` (`host`, `type`, `stagingType`)"
}
],
"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, `ruleCount` INTEGER, `checksum` TEXT, `name` TEXT NOT NULL, `source` TEXT NOT NULL, `whitelistSource` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "enabled",
"columnName": "enabled",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ruleCount",
"columnName": "ruleCount",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "checksum",
"columnName": "checksum",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "source",
"columnName": "source",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "whitelistSource",
"columnName": "whitelistSource",
"affinity": "INTEGER",
"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, '6593a50157ce88fb018043b5c151353a')"
]
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Nebulo (FDroid)</string>
<string name="app_name">Nebulo (F-Droid)</string>
</resources>
\ No newline at end of file
......@@ -36,6 +36,13 @@
</activity>
<activity android:name=".activity.MainActivity">
</activity>
<activity
android:name=".activity.SettingsActivity"
android:label="@string/menu_settings"
android:theme="@style/AppTheme_Mono_ActionBar"
android:parentActivityName=".activity.MainActivity">
</activity>
<activity
android:name=".activity.ShortcutActivity"
......
......@@ -13,7 +13,11 @@ import android.net.NetworkCapabilities
import android.net.Uri
import android.os.Build
import android.os.PowerManager
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.frostnerd.dnstunnelproxy.Decision
import com.frostnerd.dnstunnelproxy.DnsServerConfiguration
......@@ -119,6 +123,32 @@ fun Context.registerLocalReceiver(
return actualReceiver
}
fun AppCompatActivity.registerLocalReceiver(
filteredActions: List<String>,
unregisterOnDestroy:Boolean,
receiver: (intent: Intent?) -> Unit
): BroadcastReceiver {
val filter = IntentFilter()
for (filteredAction in filteredActions) {
filter.addAction(filteredAction)
}
val actualReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
receiver(intent)
}
}
val mgr = LocalBroadcastManager.getInstance(this)
mgr.registerReceiver(actualReceiver, filter)
if(unregisterOnDestroy) lifecycle.addObserver(object:LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroy() {
mgr.unregisterReceiver(actualReceiver)
}
})
return actualReceiver
}
fun Context.sendLocalBroadcast(intent: Intent) {
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
}
......@@ -162,8 +192,8 @@ fun IntArray.toStringArray(): Array<String> {
return stringArray as Array<String>
}
fun Activity.restart() {
val intent = intent
fun <T:Activity>Activity.restart(activityClass:Class<T>? = null) {
val intent = (if(activityClass != null) Intent(this, activityClass) else intent)
.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_NO_ANIMATION)
finish()
startActivity(intent)
......
......@@ -15,6 +15,9 @@ import com.github.anrwatchdog.ANRWatchDog
import io.sentry.Sentry
import io.sentry.android.AndroidSentryClientFactory
import io.sentry.event.User
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import leakcanary.LeakSentry
import java.util.*
import kotlin.system.exitProcess
......@@ -99,22 +102,23 @@ class SmokeScreen : Application() {
fun initSentry(forceEnabled: Boolean = false) {
if (!BuildConfig.DEBUG && (forceEnabled || getPreferences().crashReportingEnabled)) {
Sentry.init(
"https://fadeddb58abf408db50809922bf064cc@sentry.frostnerd.com:443/2",
AndroidSentryClientFactory(this)
)
Sentry.getContext().user = User(getPreferences().crashReportingUUID, null, null, null)
Sentry.getStoredClient().apply {
addTag("user.language", Locale.getDefault().displayLanguage)
addTag("app.database_version", AppDatabase.currentVersion.toString())
addTag("app.dns_server_name", getPreferences().dnsServerConfig.name)
addTag("app.dns_server_primary", getPreferences().dnsServerConfig.servers[0].address.formatToString())
addTag(
"app.dns_server_secondary",
getPreferences().dnsServerConfig.servers.getOrNull(1)?.address?.formatToString()
GlobalScope.launch(Dispatchers.IO) {
Sentry.init(
"https://fadeddb58abf408db50809922bf064cc@sentry.frostnerd.com:443/2",
AndroidSentryClientFactory(this@SmokeScreen)
)
addTag("app.debug", BuildConfig.DEBUG.toString())
addTag("app.installer_package", packageManager.getInstallerPackageName(packageName))
Sentry.getContext().user = User(getPreferences().crashReportingUUID, null, null, null)
Sentry.getStoredClient().apply {
addTag("user.language", Locale.getDefault().displayLanguage)
addTag("app.database_version", AppDatabase.currentVersion.toString())
addTag("app.dns_server_name", getPreferences().dnsServerConfig.name)
addTag("app.dns_server_primary", getPreferences().dnsServerConfig.servers[0].address.formatToString())
addTag(
"app.dns_server_secondary",
getPreferences().dnsServerConfig.servers.getOrNull(1)?.address?.formatToString()
)
addTag("app.installer_package", packageManager.getInstallerPackageName(packageName))
}
}
} else {
Sentry.close()
......
......@@ -48,11 +48,13 @@ import kotlin.random.Random
* You can contact the developer at daniel.wolf@frostnerd.com.
*/
class MainActivity : NavigationDrawerActivity() {
companion object {
const val BROADCAST_RELOAD_MENU = "main.reloadMenu"
}
override val drawerOverActionBar: Boolean = true
private var textColor: Int = 0
private var backgroundColor: Int = 0
private var inputElementColor: Int = 0
private var startedActivity = false
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(getPreferences().theme.layoutStyle)
......@@ -133,10 +135,16 @@ class MainActivity : NavigationDrawerActivity() {
}
}
handleDeepAction()
if(!isServiceRunning(DnsVpnService::class.java) && getPreferences().vpnServiceState == VpnServiceState.STARTED && !getPreferences().ignoreServiceKilled) {
if(!isServiceRunning(DnsVpnService::class.java) &&
getPreferences().vpnServiceState == VpnServiceState.STARTED &&
!getPreferences().ignoreServiceKilled &&
getPreferences().vpnLaunchLastVersion == BuildConfig.VERSION_CODE) {
getPreferences().vpnServiceState = VpnServiceState.STOPPED
BatteryOptimizationInfoDialog(this).show()
}
registerLocalReceiver(listOf(BROADCAST_RELOAD_MENU), true) {
reloadMenuItems()
}
}
private fun handleDeepAction() {
......@@ -164,7 +172,7 @@ class MainActivity : NavigationDrawerActivity() {
)
fragmentItem(getString(R.string.menu_settings),
iconLeft = getDrawable(R.drawable.ic_menu_settings),
fragmentCreator = singleInstanceFragment { SettingsFragment() })
fragmentCreator = singleInstanceFragment { SettingsOverviewFragment() })
if (getPreferences().queryLoggingEnabled) {
divider()
fragmentItem(getString(R.string.menu_querylogging),
......@@ -172,6 +180,7 @@ class MainActivity : NavigationDrawerActivity() {
fragmentCreator = {
QueryLogFragment()
})
}
divider()
clickableItem(getString(R.string.menu_create_shortcut),
......
......@@ -2,6 +2,7 @@ package com.frostnerd.smokescreen.activity
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.frostnerd.dnstunnelproxy.QueryListener
import com.frostnerd.smokescreen.R
import com.frostnerd.smokescreen.database.converters.StringListConverter
import com.frostnerd.smokescreen.database.entities.DnsQuery
......@@ -55,11 +56,17 @@ class QueryImportActivity: AppCompatActivity() {
val converter = StringListConverter()
for(line in iterator) {
val split = line.split(",")
val source = if(split[5].equals("false", true) || split[5].equals("true", true)) {
if (split[5].toBoolean()) QueryListener.Source.CACHE
else QueryListener.Source.UPSTREAM
} else QueryListener.Source.values().find {
it.name.equals(split[5], true)
} ?: QueryListener.Source.UPSTREAM
queries.add(DnsQuery(
name=split[0],
type = Record.TYPE.getType(split[3].toInt()),
askedServer = split[4],
fromCache = split[5].toBoolean(),
responseSource = source,
questionTime = split[6].toLong(),
responseTime = split[7].toLong(),
responses = converter.stringToList(split[8].replaceFirst("\"", "").replace(Regex("\"$"), ""))
......
package com.frostnerd.smokescreen.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.MenuItem
import com.frostnerd.lifecyclemanagement.BaseActivity
import com.frostnerd.smokescreen.fragment.SettingsFragment
import com.frostnerd.smokescreen.getPreferences
/*
* Copyright (C) 2019 Daniel Wolf (Ch4t4r)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* You can contact the developer at daniel.wolf@frostnerd.com.
*/
class SettingsActivity : BaseActivity() {
companion object {
fun showCategory(context: Context, category: Category) {
Intent(context, SettingsActivity::class.java).apply {
putExtra("category", category)
}.also {
context.startActivity(it)
}
}
}
override fun getConfiguration(): Configuration {
return Configuration.withDefaults()
}
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(getPreferences().theme.layoutStyleWithActionbar)
super.onCreate(savedInstanceState)
supportFragmentManager.beginTransaction().replace(android.R.id.content, SettingsFragment().apply {
arguments = Bundle().apply {
putSerializable("category", intent.getSerializableExtra("category"))
}
}).commit()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return if(item.itemId == android.R.id.home) {
finish()
true
} else super.onOptionsItemSelected(item)
}
enum class Category {
GENERAL, NOTIFICATION, PIN, CACHE, LOGGING, IP, NETWORK, QUERIES