Commit 227c4bea authored by Daniel Wolf's avatar Daniel Wolf
Browse files

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

parents 0fc505ec 9778c56a
......@@ -45,8 +45,4 @@ captures/
libs/
libs/**
.idea/markdown-navigator-enh.xml
.idea/markdown-navigator.xml
.idea/navEditor.xml
\ No newline at end of file
libs/**
\ No newline at end of file
......@@ -46,7 +46,7 @@ There are two options on how to use the DNS rules:<br>
You can either define your own rules - for example to block single hosts - or import *rule sources* which contain a list of rules.<br><br>
There are multiple lists maintained by independent people you can use in Nebulo.
They cover different topics, from blocking ads, tracking to blocking porn or social media.<br>
I recommend checking out [Energized](energized.pro).
I recommend checking out [Energized](https://energized.pro).
It has multiple types of lists, covering use cases mentioned above.
If you have the F-Droid version the Energized lists are already added as sources.
......
......@@ -12,13 +12,16 @@ android {
applicationId "com.frostnerd.smokescreen"
minSdkVersion 21
targetSdkVersion 30
versionCode 74
versionName "2.0.3"
versionCode 75
versionName "2.0.4"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
def sentryDSN = String.valueOf(getSystemVariableOrDefault("NEBULO_SENTRY_DSN", "dummy"))
def sentryConfigServer = String.valueOf(getSystemVariableOrDefault("NEBULO_SENTRY_CONFIGSERVER", "dummy"))
buildConfigField("Boolean", "FROM_CI", String.valueOf(getSystemVariableOrDefault("CI_COMMIT_SHORT_SHA", "") != ""))
buildConfigField("String", "COMMIT_HASH", '"' + getGitCommitHash() + '"')
buildConfigField("String", "SENTRY_DSN", '""')
buildConfigField("String", "SENTRY_DSN", '"' + sentryDSN+ '"') // A valid Sentry DSN, or "dummy"
buildConfigField("String", "SENTRY_DSN_CONFIGSERVER", '"' + sentryConfigServer + '"') // A host which returns a valid Sentry DSN on GET, or "dummy". If both are dummy Sentry is disabled.
buildConfigField("Boolean", "SHOW_DOQ", 'false')
buildConfigField("Boolean", "SHOW_INTRO", 'false')
buildConfigField("Boolean", "IN_APP_UPDATES", "false")
......@@ -99,7 +102,7 @@ android {
matchingFallbacks = ['default']
dimension "version"
versionNameSuffix "-adblock"
versionCode 75
versionCode 76
}
normal {
matchingFallbacks = ['default']
......@@ -147,10 +150,10 @@ dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2'
implementation 'com.frostnerd.utilskt:lifecycle:1.2.0'
implementation 'com.frostnerd.utilskt:lifecycle:1.2.1'
implementation 'com.frostnerd.utilskt:preferences:1.5.30' // https://git.frostnerd.com/AndroidUtils/preferenceskt
implementation 'com.frostnerd.utilskt:navigationdraweractivity:1.4.0' // https://git.frostnerd.com/AndroidUtils/navigationdraweractivity
implementation 'com.frostnerd.utilskt:encrypteddnstunnelproxy:2.1.20' // https://git.frostnerd.com/AndroidUtils/encrypteddnstunnelproxy
implementation 'com.frostnerd.utilskt:navigationdraweractivity:1.4.2' // https://git.frostnerd.com/AndroidUtils/navigationdraweractivity
implementation 'com.frostnerd.utilskt:encrypteddnstunnelproxy:2.1.21' // https://git.frostnerd.com/AndroidUtils/encrypteddnstunnelproxy
implementation 'com.frostnerd.utilskt:general:1.0.25' // https://git.frostnerd.com/AndroidUtils/generalkt
implementation 'com.frostnerd.utilskt:adapters:1.2.0' // https://git.frostnerd.com/AndroidUtils/Adapters
......@@ -169,9 +172,8 @@ dependencies {
implementation 'io.sentry:sentry-android:3.2.0'
implementation 'com.github.jorgecastilloprz:fabprogresscircle:1.01@aar'
implementation 'com.squareup.leakcanary:leaksentry:2.0-alpha-3'
leakCanaryImplementation 'com.squareup.leakcanary:leakcanary-android:2.5'
leakCanaryImplementation 'com.squareup.leakcanary:leakcanary-android:2.6'
implementation "com.squareup.okhttp3:okhttp:4.9.0"
implementation 'com.github.AppIntro:AppIntro:6.0.0'
......
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.
......@@ -11,7 +11,8 @@
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.INSTALL_SHORTCUT" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<uses-permission
android:name="android.permission.INTERACT_ACROSS_USERS"
tools:ignore="ProtectedPermissions" />
......
......@@ -38,7 +38,6 @@ import com.frostnerd.smokescreen.util.proxy.IpTablesPacketRedirector
import io.sentry.NoOpLogger
import io.sentry.android.core.BuildInfoProvider
import io.sentry.android.core.util.RootChecker
import leakcanary.LeakSentry
import java.net.Inet4Address
import java.net.Inet6Address
import java.net.InetAddress
......@@ -63,6 +62,7 @@ import java.util.logging.Level
* You can contact the developer at daniel.wolf@frostnerd.com.
*/
@Suppress("DEPRECATION")
fun Context.canUseFingerprintAuthentication(): Boolean {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return false
val mgr = getSystemService(Context.FINGERPRINT_SERVICE) as? FingerprintManager
......@@ -73,6 +73,7 @@ fun Context.canUseFingerprintAuthentication(): Boolean {
return true
}
@Suppress("unused")
fun Context.registerReceiver(intentFilter: IntentFilter, receiver: (intent: Intent?) -> Unit): BroadcastReceiver {
val actualReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
......@@ -158,6 +159,7 @@ fun AppCompatActivity.registerLocalReceiver(
mgr.registerReceiver(actualReceiver, filter)
if(unregisterOnDestroy) lifecycle.addObserver(object:LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
@Suppress("unused")
fun onDestroy() {
mgr.unregisterReceiver(actualReceiver)
}
......@@ -399,7 +401,7 @@ private fun createHttpsUpstreamAddress(url: String): HttpsUpstreamAddress {
}
private fun createTlsUpstreamAddress(host: String): TLSUpstreamAddress {
var parsedHost = ""
val parsedHost:String
var port: Int? = null
if (host.contains(":")) {
parsedHost = host.split(":")[0]
......@@ -410,18 +412,6 @@ private fun createTlsUpstreamAddress(host: String): TLSUpstreamAddress {
else TLSUpstreamAddress(parsedHost)
}
fun LeakSentry.watchIfEnabled(watchedInstance: Any) {
if(BuildConfig.LEAK_DETECTION) {
refWatcher.watch(watchedInstance)
}
}
fun LeakSentry.watchIfEnabled(watchedInstance: Any, name:String) {
if(BuildConfig.LEAK_DETECTION) {
refWatcher.watch(watchedInstance, name)
}
}
fun String.equalsAny(vararg options:String, ignoreCase:Boolean = false):Boolean {
return options.any {
it.equals(this, ignoreCase)
......
......@@ -11,7 +11,6 @@ import io.sentry.Sentry
import io.sentry.SentryEvent
import io.sentry.SentryLevel
import io.sentry.protocol.Message
import leakcanary.LeakSentry
import java.io.*
import java.text.SimpleDateFormat
import java.util.*
......@@ -65,7 +64,6 @@ private fun Context.logErrorSentry(e: Throwable, extras: Map<String, String>? =
this.message = e.message
}
level = SentryLevel.ERROR
setExtra("retainedInstanceCount", LeakSentry.refWatcher.retainedInstanceCount)
})
} else if (getPreferences().crashreportingType == Crashreporting.FULL && extras != null && extras.isNotEmpty()) {
log("Sending exception with extras")
......@@ -78,7 +76,6 @@ private fun Context.logErrorSentry(e: Throwable, extras: Map<String, String>? =
extras.forEach { (key, value) ->
setTag(key, value)
}
setExtra("retainedInstanceCount", LeakSentry.refWatcher.retainedInstanceCount)
})
} else {
log("Sending exception to Sentry without extras")
......@@ -121,7 +118,7 @@ fun Context.log(e: Throwable, extras: Map<String, String>? = null) {
"${Logger.logFileNameTimeStampFormatter.format(System.currentTimeMillis())}.err"
)
if (errorFile.createNewFile()) {
if (errorFile.tryCreateNewFile()) {
val writer = BufferedWriter(FileWriter(errorFile, false))
writer.write("App version: ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE}, Commit: ${BuildConfig.COMMIT_HASH})\n")
writer.write("Android SDK version: ${Build.VERSION.SDK_INT} (${Build.VERSION.RELEASE} - ${Build.VERSION.CODENAME})\n")
......@@ -141,7 +138,7 @@ fun Context.closeLogger() {
fun Context.deleteAllLogs() {
if (Logger.isOpen())
Logger.getInstance(this).destroy()
Logger.getLogDir(this).listFiles().forEach {
Logger.getLogDir(this).listFiles()!!.forEach {
it.delete()
}
}
......@@ -181,7 +178,7 @@ class Logger private constructor(context: Context) {
"${id}_${logFileNameTimeStampFormatter.format(System.currentTimeMillis())}.log"
)
logDir.mkdirs()
logFile.createNewFile()
logFile.tryCreateNewFile()
fileWriter = BufferedWriter(FileWriter(logFile, false))
log("App version: ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})")
......@@ -309,8 +306,12 @@ class Logger private constructor(context: Context) {
if (printToConsole) {
(oldSystemOut ?: System.out).println(textBuilder)
}
fileWriter.write(textBuilder.toString())
fileWriter.flush()
try {
fileWriter.write(textBuilder.toString())
fileWriter.flush()
} catch (ex:Throwable) {
}
}
}
}
......@@ -325,7 +326,7 @@ class Logger private constructor(context: Context) {
"${id}_${logFileNameTimeStampFormatter.format(System.currentTimeMillis())}.err"
)
if (errorFile.createNewFile()) {
if (errorFile.tryCreateNewFile()) {
val writer = BufferedWriter(FileWriter(errorFile, false))
writer.write("App version: ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})\n")
writer.write("Android SDK version: ${Build.VERSION.SDK_INT} (${Build.VERSION.RELEASE} - ${Build.VERSION.CODENAME})\n")
......@@ -343,6 +344,14 @@ class Logger private constructor(context: Context) {
}
}
fun File.tryCreateNewFile():Boolean {
return try {
createNewFile()
} catch (ex:Throwable) {
false
}
}
fun Context.zipAllLogFiles(): File? {
val dir = Logger.getLogDir(this)
if (!dir.canWrite() || !dir.canRead()) return null
......@@ -351,7 +360,7 @@ fun Context.zipAllLogFiles(): File? {
if (zipFile.exists() && (!zipFile.canRead() || !zipFile.canWrite())) return null
if (zipFile.exists()) zipFile.delete()
var filesToBeZipped = dir.listFiles()
var filesToBeZipped = dir.listFiles()!!
val dest = FileOutputStream(zipFile)
val out = ZipOutputStream(BufferedOutputStream(dest))
val buffer = ByteArray(2048)
......
......@@ -24,16 +24,13 @@ import io.sentry.Sentry
import io.sentry.SentryOptions
import io.sentry.android.core.*
import io.sentry.protocol.User
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import leakcanary.LeakSentry
import okhttp3.*
import org.minidns.dnsmessage.DnsMessage
import org.minidns.dnsmessage.Question
import org.minidns.record.A
import org.minidns.record.AAAA
import org.minidns.record.Record
import java.net.InetAddress
import java.io.IOException
import java.util.*
import kotlin.system.exitProcess
......@@ -68,15 +65,15 @@ class SmokeScreen : Application() {
else {
context.log("Using fallback server: $to")
val configs = to.serverConfigurations.values
AddressCreator.globalResolve = {
val responsesIpv4 = configs.random().query(Question(it, Record.TYPE.A))?.takeIf {
AddressCreator.globalResolve = { domain ->
val responsesIpv4 = configs.random().query(Question(domain, Record.TYPE.A))?.takeIf {
it.responseCode == DnsMessage.RESPONSE_CODE.NO_ERROR
}?.answerSection?.map {
it.payload as A
}?.map {
it.inetAddress
}
val responsesIpv6 = configs.random().query(Question(it, Record.TYPE.AAAA))?.takeIf {
val responsesIpv6 = configs.random().query(Question(domain, Record.TYPE.AAAA))?.takeIf {
it.responseCode == DnsMessage.RESPONSE_CODE.NO_ERROR
}?.answerSection?.map {
it.payload as AAAA
......@@ -135,16 +132,14 @@ class SmokeScreen : Application() {
}
override fun onCreate() {
if(!BuildConfig.LEAK_DETECTION) LeakSentry.config = LeakSentry.config.copy(enabled = false)
initSentry()
defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler()
Thread.setDefaultUncaughtExceptionHandler(customUncaughtExceptionHandler)
super.onCreate()
log("Application created.")
LeakSentry.watchIfEnabled(this)
handleFallbackDns()
loadKnownDNSServers()
AbstractQuicDnsHandle.installProvider(this, {})
AbstractQuicDnsHandle.installProvider(this) {}
}
private fun handleFallbackDns() {
......@@ -175,75 +170,122 @@ class SmokeScreen : Application() {
}
fun initSentry(forceStatus: Status = Status.NONE) {
if (!BuildConfig.DEBUG && BuildConfig.SENTRY_DSN != "dummy") {
log("Sentry will be initialized.")
GlobalScope.launch(Dispatchers.IO) {
log("Initializing Sentry.")
sentryReady = false
try {
val hostName = InetAddress.getLocalHost().hostName
if(!hostName.startsWith("mars-sandbox", true)) {
val enabledType = getPreferences().crashreportingType
if (forceStatus != Status.DATASAVING && (enabledType == Crashreporting.FULL || forceStatus == Status.ENABLED)) {
// Enable Sentry in full mode
// This passes some device-related data, but nothing which allows user actions to be tracked across the app
// Info: Some data is attached by the AndroidEventBuilderHelper class, which is present by default
val enabledType = getPreferences().crashreportingType
if (forceStatus == Status.NONE &&
enabledType == Crashreporting.OFF) return
log("Maybe initializing Sentry")
getSentryDSN { resolvedDSN ->
log("Initializing Sentry.")
sentryReady = false
try {
if (sentryReady) {
log("Reinitializing Sentry with new DSN")
SentryAndroid.init(this@SmokeScreen) {
it.dsn = resolvedDSN
}
} else {
if (forceStatus != Status.DATASAVING && (enabledType == Crashreporting.FULL || forceStatus == Status.ENABLED)) {
// Enable Sentry in full mode
// This passes some device-related data, but nothing which allows user actions to be tracked across the app
// Info: Some data is attached by the AndroidEventBuilderHelper class, which is present by default
SentryAndroid.init(this@SmokeScreen) {
it.dsn = BuildConfig.SENTRY_DSN
}
Sentry.setUser(User().apply {
this.username = getPreferences().crashReportingUUID
})
Sentry.setTag("user.language", Locale.getDefault().displayLanguage)
Sentry.setTag(
"app.database_version",
AppDatabase.currentVersion.toString()
)
Sentry.setTag(
"app.dns_server_name",
getPreferences().dnsServerConfig.name
)
Sentry.setTag(
"app.dns_server_primary",
getPreferences().dnsServerConfig.servers[0].address.formatToString()
)
Sentry.setTag(
"app.dns_server_secondary",
getPreferences().dnsServerConfig.servers.getOrNull(1)?.address?.formatToString()
?: ""
)
Sentry.setTag(
"app.installer_package",
packageManager.getInstallerPackageName(packageName) ?: ""
)
Sentry.setTag("richdata", "true")
Sentry.setTag("app.fromCi", BuildConfig.FROM_CI.toString())
Sentry.setTag("app.commit", BuildConfig.COMMIT_HASH)
sentryReady = true
} else if (enabledType == Crashreporting.MINIMAL || forceStatus == Status.DATASAVING) {
// Inits Sentry in datasaving mode
// Only data absolutely necessary is transmitted (Android version, app version).
// Only crashes will be reported, no regular events.
SentryAndroid.init(this@SmokeScreen) {
it.dsn = BuildConfig.SENTRY_DSN
setupSentryForDatasaving(it)
}
Sentry.setUser(User().apply {
this.username = "anon-" + BuildConfig.VERSION_CODE
})
Sentry.setTag("richdata", "false")
Sentry.setTag("dist", BuildConfig.VERSION_CODE.toString())
Sentry.setTag("app.commit", BuildConfig.COMMIT_HASH)
Sentry.setTag("app.fromCi", BuildConfig.FROM_CI.toString())
sentryReady = true
SentryAndroid.init(this@SmokeScreen) {
it.dsn = resolvedDSN
}
Sentry.setUser(User().apply {
this.username = getPreferences().crashReportingUUID
})
Sentry.setTag("user.language", Locale.getDefault().displayLanguage)
Sentry.setTag(
"app.database_version",
AppDatabase.currentVersion.toString()
)
Sentry.setTag(
"app.dns_server_name",
getPreferences().dnsServerConfig.name
)
Sentry.setTag(
"app.dns_server_primary",
getPreferences().dnsServerConfig.servers[0].address.formatToString()
)
Sentry.setTag(
"app.dns_server_secondary",
getPreferences().dnsServerConfig.servers.getOrNull(1)?.address?.formatToString()
?: ""
)
Sentry.setTag(
"app.installer_package",
packageManager.getInstallerPackageName(packageName) ?: ""
)
Sentry.setTag("richdata", "true")
Sentry.setTag("app.fromCi", BuildConfig.FROM_CI.toString())
Sentry.setTag("app.commit", BuildConfig.COMMIT_HASH)
sentryReady = true
} else if (enabledType == Crashreporting.MINIMAL || forceStatus == Status.DATASAVING) {
// Inits Sentry in datasaving mode
// Only data absolutely necessary is transmitted (Android version, app version).
// Only crashes will be reported, no regular events.
SentryAndroid.init(this@SmokeScreen) {
it.dsn = resolvedDSN
setupSentryForDatasaving(it)
}
Sentry.setUser(User().apply {
this.username = "anon-" + BuildConfig.VERSION_CODE
})
Sentry.setTag("richdata", "false")
Sentry.setTag("dist", BuildConfig.VERSION_CODE.toString())
Sentry.setTag("app.commit", BuildConfig.COMMIT_HASH)
Sentry.setTag("app.fromCi", BuildConfig.FROM_CI.toString())
sentryReady = true
}
} catch(ex:Throwable) {
ex.printStackTrace()
}
log("Sentry ready.")
} catch (ex: Throwable) {
log("Creating Sentry errored")
log(ex)
}
}
}
private fun getSentryDSN(then:(dsn:String) -> Unit) {
if(BuildConfig.DEBUG) return
try {
val primaryDSN = BuildConfig.SENTRY_DSN
val configServer = BuildConfig.SENTRY_DSN_CONFIGSERVER
if(primaryDSN.contains("@")) then(primaryDSN)
else log("Primary Sentry DSN not set, maybe retrieving from server")
if(configServer.isNotBlank() && configServer.startsWith("http") && !configServer.contains("@")) {
log("Dynamically retrieving Sentry DSN from $configServer")
val request = Request.Builder().url(configServer).build()
OkHttpClient.Builder().build().newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
log("Sentry DSN retrieval failed with error: ${e.message}")
}
override fun onResponse(call: Call, response: Response) {
println("RESPONSE")
if (!response.isSuccessful) {
log("Sentry DSN retrieval failed with status ${response.code}")
}
val retrievedDSN = response.body?.string()?.trim()
if (retrievedDSN.isNullOrBlank() || !retrievedDSN.contains("@") || !retrievedDSN.startsWith(
"http"
)
) {
log("Sentry DSN retrieval returned invalid DSN '$retrievedDSN'.")
} else {
if (retrievedDSN != primaryDSN) {
log("Sentry DSN successfuly resolved to '$retrievedDSN'")
then(retrievedDSN)
} else log("Retrieved Sentry DSN is the same as configured DSN, not re-configuring")
}
}
})
}
} catch (ex:Throwable) {
log("Getting Sentry DSN errorerd")
log(ex)
}
}
......
......@@ -5,9 +5,12 @@ import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.net.*
import android.os.Build
import android.os.Bundle
import android.os.PowerManager
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import com.frostnerd.general.service.isServiceRunning
import com.frostnerd.lifecyclemanagement.launchWithLifecycle
import com.frostnerd.navigationdraweractivity.NavigationDrawerActivity
......@@ -92,7 +95,7 @@ class MainActivity : NavigationDrawerActivity() {
setTheme(getPreferences().theme.layoutStyle)
super.onCreate(savedInstanceState)
Notifications.createAllChannels(this)
setCardView { viewParent, suggestedHeight ->
setCardView { viewParent, _ ->
val view = layoutInflater.inflate(R.layout.menu_cardview, viewParent, false)
val update = {
launchWithLifecycle {
......@@ -209,7 +212,9 @@ class MainActivity : NavigationDrawerActivity() {
!getPreferences().ignoreServiceKilled &&
getPreferences().vpnLaunchLastVersion == BuildConfig.VERSION_CODE) {
getPreferences().vpnServiceState = VpnServiceState.STOPPED
BatteryOptimizationInfoDialog(this).show()
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M|| !(getSystemService(POWER_SERVICE) as PowerManager).isIgnoringBatteryOptimizations(packageName)) {
BatteryOptimizationInfoDialog(this).show()
}
}
registerLocalReceiver(listOf(BROADCAST_RELOAD_MENU), true) {
reloadMenuItems()
......@@ -279,11 +284,11 @@ class MainActivity : NavigationDrawerActivity() {
override fun createDrawerItems(): MutableList<DrawerItem> {
return createMenu {
fragmentItem(getString(R.string.menu_main),
iconLeft = getDrawable(R.drawable.ic_menu_dnsoverhttps),
iconLeft = ContextCompat.getDrawable(this@MainActivity, R.drawable.ic_menu_dnsoverhttps),
fragmentCreator = singleInstanceFragment { MainFragment() }
)
fragmentItem(getString(R.string.menu_settings),
iconLeft = getDrawable(R.drawable.ic_menu_settings),
iconLeft = ContextCompat.getDrawable(this@MainActivity, R.drawable.ic_menu_settings),
fragmentCreator = singleInstanceFragment { args ->
SettingsOverviewFragment().also {
it.arguments = args
......@@ -292,7 +297,7 @@ class MainActivity : NavigationDrawerActivity() {
if (getPreferences().queryLoggingEnabled) {
divider()
fragmentItem(getString(R.string.menu_querylogging),
iconLeft = getDrawable(R.drawable.ic_eye),
iconLeft = ContextCompat.getDrawable(this@MainActivity, R.drawable.ic_eye),
fragmentCreator = {
QueryLogFragment()
})
......@@ -300,7 +305,7 @@ class MainActivity : NavigationDrawerActivity() {
}
divider()
clickableItem(getString(R.string.menu_create_shortcut),
iconLeft = getDrawable(R.drawable.ic_external_link),
iconLeft = ContextCompat.getDrawable(this@MainActivity, R.drawable.ic_external_link),
onLongClick = null,
onSimpleClick = { _, _, _ ->
ServerChoosalDialog(this@MainActivity, onEntrySelected = {
......@@ -309,14 +314,14 @@ class MainActivity : NavigationDrawerActivity() {
false
})
fragmentItem(getString(R.string.button_main_dnsrules),
iconLeft = getDrawable(R.drawable.ic_view_list),
iconLeft = ContextCompat.getDrawable(this@MainActivity, R.drawable.ic_view_list),
fragmentCreator = {
DnsRuleFragment()
})
divider()
if (isPackageInstalled(this@MainActivity, "com.android.vending")) {
clickableItem(getString(R.string.menu_rate),
iconLeft = getDrawable(R.drawable.ic_star),
iconLeft = ContextCompat.getDrawable(this@MainActivity, R.drawable.ic_star),
onLongClick = null,
onSimpleClick = { _, _, _ ->
AlertDialog.Builder(this@MainActivity, getPreferences().theme.dialogStyle)
......@@ -333,7 +338,7 @@ class MainActivity : NavigationDrawerActivity() {
}
if (isPackageInstalled(this@MainActivity, "org.fdroid.fdroid")) {
clickableItem(getString(R.string.menu_show_on_fdroid),
iconLeft = getDrawable(R.drawable.ic_adb),
iconLeft = ContextCompat.getDrawable(this@MainActivity, R.drawable.ic_adb),
onLongClick = null,
onSimpleClick = { _, _, _ ->
startActivity(
......@@ -347,13 +352,13 @@ class MainActivity : NavigationDrawerActivity() {
)
}
fragmentItem(getString(R.string.menu_about),
iconLeft = getDrawable(R.drawable.ic_info),
iconLeft = ContextCompat.getDrawable(this@MainActivity, R.drawable.ic_info),
fragmentCreator = singleInstanceFragment { AboutFragment() })
}
}
override fun onBackPressed() {
val fragment = currentFragment
val fragment = supportFragmentManager.findFragmentById(R.id.drawerContent)