Commit 99d4a671 authored by Daniel Wolf's avatar Daniel Wolf
Browse files

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

parents a4d06d7e 7805cb24
......@@ -25,13 +25,13 @@ android {
applicationId "com.frostnerd.smokescreen"
minSdkVersion 21
targetSdkVersion 29
versionCode 55
versionName "1.2.1"
versionCode 57
versionName "1.2.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
buildConfigField("Boolean", "FROM_CI", String.valueOf(getSystemVariableOrDefault("CI_COMMIT_SHORT_SHA", "") != ""))
buildConfigField("String", "COMMIT_HASH", '"' + getGitCommitHash() + '"')
buildConfigField("String", "SENTRY_DSN", '"' + getSystemVariableOrDefault("SENTRY_DSN", "dummy") + '"')
buildConfigField("String", "SENTRY_DSN", '"https://fadeddb58abf408db50809922bf064cc@sentry.frostnerd.com:443/2"')
kapt {
arguments {
......@@ -124,7 +124,7 @@ dependencies {
implementation 'com.frostnerd.utilskt:preferences:1.5.17' // https://git.frostnerd.com/AndroidUtils/preferenceskt
implementation 'com.frostnerd.utilskt:navigationdraweractivity:1.3.29' // https://git.frostnerd.com/AndroidUtils/navigationdraweractivity
implementation 'com.frostnerd.utilskt:encrypteddnstunnelproxy:1.5.178' // https://git.frostnerd.com/AndroidUtils/encrypteddnstunnelproxy
implementation 'com.frostnerd.utilskt:encrypteddnstunnelproxy:1.5.181' // https://git.frostnerd.com/AndroidUtils/encrypteddnstunnelproxy
implementation 'com.frostnerd.utilskt:general:1.0.19' // https://git.frostnerd.com/AndroidUtils/generalkt
implementation 'com.frostnerd.utilskt:adapters:1.1.6' // https://git.frostnerd.com/AndroidUtils/Adapters
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -30,7 +30,10 @@ import com.frostnerd.smokescreen.util.LanguageContextWrapper
class SettingsActivity : BaseActivity() {
companion object {
private var category:Category? = null
fun showCategory(context: Context, category: Category) {
this.category = category
Intent(context, SettingsActivity::class.java).apply {
putExtra("category", category)
}.also {
......@@ -52,7 +55,7 @@ class SettingsActivity : BaseActivity() {
super.onCreate(savedInstanceState)
supportFragmentManager.beginTransaction().replace(android.R.id.content, SettingsFragment().apply {
arguments = Bundle().apply {
putSerializable("category", intent.getSerializableExtra("category")!!)
putSerializable("category", intent.getSerializableExtra("category") ?: category ?: Category.GENERAL)
}
}).commit()
}
......
......@@ -55,7 +55,11 @@ class BatteryOptimizationInfoDialog(context: Context) :
context.getString(R.string.menu_settings)
) { dialog, _ ->
dialog.dismiss()
context.startActivity(settingsIntent)
try {
context.startActivity(settingsIntent)
} catch (e: SecurityException) {
Toast.makeText(context, R.string.error_cannot_open_settings, Toast.LENGTH_LONG).show()
}
}
} else {
setButton(
......
......@@ -286,14 +286,14 @@ class NewServerDialog(
private fun addUrlTextWatcher(input: TextInputLayout, editText: TextInputEditText, emptyAllowed: Boolean) {
editText.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {
val valid =
(emptyAllowed && s.isBlank()) || (dnsOverHttps && SERVER_URL_REGEX.matches(s.toString())) || (!dnsOverHttps && TLS_REGEX.matches(
s.toString()
))
var valid = (emptyAllowed && s.isBlank())
valid = valid || (!s.isBlank() && dnsOverHttps && SERVER_URL_REGEX.matches(s.toString()))
valid = valid || (!s.isBlank() && !dnsOverHttps && TLS_REGEX.matches(s.toString()))
input.error = if (valid) {
null
} else context.getString(R.string.error_invalid_url)
} else if(dnsOverHttps) context.getString(R.string.error_invalid_url)
else context.getString(R.string.error_invalid_host)
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
......
......@@ -225,19 +225,19 @@ class DnsRuleFragment : Fragment() {
notifyRulesChanged()
}, editSource = { hostSource ->
NewHostSourceDialog(context!!, onSourceCreated = { newSource ->
val currentSource = getDatabase().hostSourceDao().findById(hostSource.id)!!.apply {
getDatabase().hostSourceDao().findById(hostSource.id)?.apply {
this.name = newSource.name
this.source = newSource.source
this.whitelistSource = newSource.whitelistSource
}
getDatabase().hostSourceDao().update(currentSource)
}?.also {
getDatabase().hostSourceDao().update(it)
val index = sourceAdapterList.indexOf(hostSource).takeIf { it >= 0 } ?: sourceAdapterList.indexOfFirst { it.id == hostSource.id }
sourceAdapterList[index] = currentSource
sourceRuleCount[currentSource] = sourceRuleCount[hostSource]
sourceRuleCount.remove(hostSource)
sourceAdapter.notifyItemChanged(index)
val index = sourceAdapterList.indexOf(hostSource).takeIf { it >= 0 } ?: sourceAdapterList.indexOfFirst { it.id == hostSource.id }
sourceAdapterList[index] = it
sourceRuleCount[it] = sourceRuleCount[hostSource]
sourceRuleCount.remove(hostSource)
sourceAdapter.notifyItemChanged(index)
}
}, showFileChooser = { callback ->
fileChosenCallback = callback
startActivityForResult(Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
......
......@@ -3,10 +3,7 @@ package com.frostnerd.smokescreen.service
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.content.*
import android.content.pm.PackageManager
import android.net.*
import android.os.*
......@@ -98,6 +95,8 @@ class DnsVpnService : VpnService(), Runnable {
private var lastVPNStopTime:Long? = null
private val coroutineScope:CoroutineContext = SupervisorJob()
private var queryCount = 0
private var dnsCache:SimpleDnsCache? = null
private var localResolver:LocalResolver? = null
private val addressResolveScope:CoroutineScope by lazy {
CoroutineScope(newSingleThreadContext("service-resolve-retry"))
}
......@@ -525,6 +524,15 @@ class DnsVpnService : VpnService(), Runnable {
return if (destroyed) Service.START_NOT_STICKY else Service.START_STICKY
}
override fun onTrimMemory(level: Int) {
if(level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND ||
level == ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL ||
level == ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW) {
dnsCache?.clear()
localResolver?.cleanup()
}
}
private fun setServerConfiguration(intent: Intent?) {
log("Updating server configuration..")
userServerConfig = BackgroundVpnConfigureActivity.readServerInfoFromIntent(intent)
......@@ -734,6 +742,17 @@ class DnsVpnService : VpnService(), Runnable {
Handler(Looper.getMainLooper()).postDelayed({
BackgroundVpnConfigureActivity.prepareVpn(this, userServerConfig)
}, 250)
} else if(getPreferences().showNotificationOnRevoked){
NotificationCompat.Builder(this, Notifications.getHighPriorityChannelId(this)).apply {
setSmallIcon(R.drawable.ic_cloud_warn)
setContentTitle(getString(R.string.notification_service_revoked_title))
setContentText(getString(R.string.notification_service_revoked_message))
setContentIntent(PendingIntent.getActivity(this@DnsVpnService, RequestCodes.RESTART_AFTER_REVOKE, Intent(this@DnsVpnService, BackgroundVpnConfigureActivity::class.java), PendingIntent.FLAG_CANCEL_CURRENT))
setAutoCancel(true)
priority = NotificationCompat.PRIORITY_HIGH
}.build().also {
(getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).notify(Notifications.ID_SERVICE_REVOKED, it)
}
}
}
......@@ -1020,12 +1039,15 @@ class DnsVpnService : VpnService(), Runnable {
}
log("Creating DNS proxy with ${1 + handles.size} handles")
dnsCache = createDnsCache()
localResolver = createLocalResolver()
dnsProxy = SmokeProxy(
defaultHandle!!,
handles + createProxyBypassHandlers(),
createDnsCache(),
dnsCache,
createQueryLogger(),
createLocalResolver()
localResolver
)
log("DnsProxy created, creating VPN proxy")
vpnProxy = RetryingVPNTunnelProxy(dnsProxy!!, vpnService = this, coroutineScope = CoroutineScope(
......@@ -1043,6 +1065,7 @@ class DnsVpnService : VpnService(), Runnable {
log("VPN proxy started.")
currentTrafficStats = vpnProxy?.trafficStats
LocalBroadcastManager.getInstance(this).sendBroadcast(Intent(BROADCAST_VPN_ACTIVE))
(getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).cancel(Notifications.ID_SERVICE_REVOKED)
}
private fun createQueryLogger(): QueryListener? {
......
......@@ -36,6 +36,7 @@ class Notifications {
const val ID_DNSRULE_EXPORT_FINISHED = 7
const val ID_PIN = 8
const val ID_SERVICE_KILLED = 9
const val ID_SERVICE_REVOKED = 10
const val ID_VPN_RESTART = 999
......@@ -104,6 +105,23 @@ class Notifications {
}
return "pinchannel"
}
fun getHighPriorityChannelId(context: Context):String {
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
val channel = NotificationChannel(
"high_priority",
context.getString(R.string.notification_channel_high_priorty),
NotificationManager.IMPORTANCE_HIGH
)
channel.enableLights(true)
channel.enableVibration(true)
channel.description = context.getString(R.string.notification_channel_high_priorty)
channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
channel.setBypassDnd(true)
(context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(channel)
}
return "high_priority"
}
}
}
......@@ -120,4 +138,5 @@ object RequestCodes {
val RULE_EXPORT_ABORT = 8
val PIN_NOTIFICATION = 9
val REQUEST_CODE_IGNORE_SERVICE_KILLED = 10
val RESTART_AFTER_REVOKE = 11
}
\ No newline at end of file
......@@ -182,6 +182,7 @@ class AppSettingsSharedPreferences(context: Context) : AppSettings, SimpleTypedP
override var hideNotificationIcon: Boolean by booleanPref("hide_notification_icon", false)
override var allowPauseInNotification: Boolean by booleanPref("notification_allow_pause", true)
override var allowStopInNotification: Boolean by booleanPref("notification_allow_stop", true)
var showNotificationOnRevoked:Boolean by booleanPref("show_vpn_revoked_notification", true)
override var enablePin:Boolean by booleanPref("enable_pin", false)
override var allowFingerprintForPin:Boolean by booleanPref("pin_allow_fingerprint", true)
......
......@@ -236,7 +236,13 @@ class DnsRuleResolver(context: Context) : LocalResolver(false) {
} ?: throw IllegalStateException()
}
override fun cleanup() {}
override fun cleanup() {
cachedWildcardWhitelisted.clear()
cachedNonWildcardWhitelisted.clear()
cachedResolved.clear()
cachedWildcardResolved.clear()
cachedNonIncluded.clear()
}
// Handle CNAME Cloaking
// Does not need to handle whitelist as the query has already been forwarded
......
......@@ -365,5 +365,14 @@
<item>@string/changelog_build_31_4</item>
</string-array>
<string name="changelog_build_58_1">The primary host/url could be empty when adding a server</string>
<string name="changelog_build_58_2">A notification is now shown when Nebulo loses permission to the VPN</string>
<string-array name="changelog_build_58">
<item>Version 1.2.3 (Build 58)</item>
<item>@string/changelog_build_58_1</item>
<item>@string/changelog_build_58_2</item>
<item>@string/changelog_build_31_4</item>
</string-array>
<string name="changelog_build_55_4">The query log now loads the queries in chunks instead of all at once</string>
</resources>
......@@ -12,6 +12,9 @@
<string name="notification_channel_pin">Pin-related channel</string>
<string name="notification_channel_pin_description">Channel used for notifications related to the pin, i.e. when it has to be provided</string>
<string name="notification_channel_high_priorty">High priority</string>
<string name="notification_channel_high_priorty_description">Channel for high priority notifications for example when the app loses permission to the VPN</string>
<string name="notification_simple_text">Current server: %1$s</string>
<string name="notification_main_text">Queries are sent to %1$s\n\n%2$d Apps are excluded from DoH/DoT\n%3$d entries in the cache</string>
<string name="notification_main_text_with_secondary">Queries are sent to %1$s and %2$s\n\n%3$d Apps are excluded from DoH/DoT\n%4$d entries in the cache</string>
......@@ -51,4 +54,7 @@
<string name="notification_service_killed_title">Information about battery optimization</string>
<string name="notification_service_killed_message">It seems like the app was previously stopped by the system. Click here to learn more.</string>
<string name="notification_service_killed_ignore">Ignore</string>
<string name="notification_service_revoked_title">VPN permission revoked by system</string>
<string name="notification_service_revoked_message">Click here to restart</string>
</resources>
\ No newline at end of file
......@@ -36,6 +36,12 @@
<string name="title_notification_allow_pause">Allow pausing</string>
<string name="summary_notification_allow_pause">Allow the app to be paused from the notification</string>
<string name="title_show_noconnection_notification">Notification on no connection</string>
<string name="summary_show_noconnection_notification">Show a notification when the app has no or a bad connection</string>
<string name="title_show_revoked_notification">Notification on permission lost</string>
<string name="summary_show_revoked_notification">Show a notification when Nebulo loses permission to the VPN</string>
<string name="preference_category_pin">PIN protection</string>
......@@ -134,9 +140,6 @@
<string name="title_pause_on_captiveportal">Pause on captive portal</string>
<string name="summary_pause_on_captiveportal">Don\'t use DoH if a captive portal is detected. It will automatically be resumed shortly after logging in.\nDisabling it might prevent you from opening the captive portal site to log in.</string>
<string name="title_show_noconnection_notification">Notification on no connection</string>
<string name="summary_show_noconnection_notification">Show a notification when the app has no or a bad connection</string>
<string name="preference_category_querylogging">Query logging</string>
......
......@@ -18,9 +18,11 @@
<string name="all_close">Close</string>
<string name="error_invalid_url">Please provide a valid url.</string>
<string name="error_invalid_host">Please provide a valid host.</string>
<string name="error_invalid_pin">Please provide your PIN.</string>
<string name="error_invalid_servername">Please provide a name.</string>
<string name="error_webview_missing">WebView package not found, is it currently updating or removed?</string>
<string name="error_cannot_open_settings">Cannot open system settings.</string>
<string name="main_dnssurveillance_infotext">Be aware that the DNS server you are connected to is able to record which sites you are visiting. Check the respective privacy statements for more information.</string>
<string name="main_dnssurveillance_privacystatement">Privacy statement of %1s</string>
......
......@@ -37,5 +37,11 @@
android:key="show_no_connection_notification"
android:summary="@string/summary_show_noconnection_notification"
android:title="@string/title_show_noconnection_notification" />
<CheckBoxPreference
android:defaultValue="true"
android:key="show_vpn_revoked_notification"
android:summary="@string/summary_show_revoked_notification"
android:title="@string/title_show_revoked_notification" />
</PreferenceCategory>
</PreferenceScreen>
\ No newline at end of file
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