Loading app/src/main/AndroidManifest.xml +13 −5 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ <!-- getting the WiFi name (for "sync in Wifi only") requires - coarse location (Android 8.1) - fine location (Android 10) --> <uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION"/> <!-- required since Android 10 to get the WiFi name while in background (= while syncing) --> <uses-permission-sdk-23 android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/> Loading Loading @@ -88,11 +89,18 @@ android:name=".ui.account.AccountActivity" android:parentActivityName=".ui.AccountsActivity" android:theme="@style/AppTheme.NoActionBar"/> <activity android:name=".ui.account.SettingsActivity"/> <activity android:name=".ui.CreateAddressBookActivity" <activity android:name=".ui.CreateAddressBookActivity" android:label="@string/create_addressbook"/> <activity android:name=".ui.CreateCalendarActivity" <activity android:name=".ui.CreateCalendarActivity" android:label="@string/create_calendar"/> <activity android:name=".ui.account.SettingsActivity" /> <activity android:name=".ui.account.WifiPermissionsActivity" android:label="@string/wifi_permissions_label" android:parentActivityName=".ui.account.SettingsActivity" /> <activity android:name=".ui.DebugInfoActivity" Loading app/src/main/java/at/bitfire/davdroid/PermissionUtils.kt +39 −0 Original line number Diff line number Diff line Loading @@ -5,9 +5,14 @@ import android.app.PendingIntent import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.location.LocationManager import android.net.Uri import android.os.Build import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.core.content.ContextCompat import androidx.core.location.LocationManagerCompat import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.ui.NotificationUtils import at.bitfire.davdroid.ui.PermissionsActivity Loading @@ -22,6 +27,31 @@ object PermissionUtils { Manifest.permission.WRITE_CALENDAR ) val WIFI_SSID_PERMISSIONS = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION) else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION) else arrayOf() /** * Checks whether all conditions to access the current WiFi's SSID are met: * * 1. location permissions ([WIFI_SSID_PERMISSIONS]) granted * 2. location enabled * * @return *true* if SSID can be obtained; *false* if the SSID will be <unknown> or something like that */ fun canAccessWifiSsid(context: Context): Boolean { val locationEnabled = ContextCompat.getSystemService(context, LocationManager::class.java)?.let { locationManager -> LocationManagerCompat.isLocationEnabled(locationManager) } ?: /* location feature not available on this device */ false return havePermissions(context, WIFI_SSID_PERMISSIONS) && locationEnabled } /** * Checks whether at least one of the given permissions is granted. * Loading Loading @@ -64,4 +94,13 @@ object PermissionUtils { .notify(NotificationUtils.NOTIFY_PERMISSIONS, notify) } fun showAppSettings(context: Context) { val intent = Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.fromParts("package", BuildConfig.APPLICATION_ID, null)) if (intent.resolveActivity(context.packageManager) != null) context.startActivity(intent) else Logger.log.warning("App settings Intent not resolvable") } } No newline at end of file app/src/main/java/at/bitfire/davdroid/settings/AccountSettings.kt +8 −4 Original line number Diff line number Diff line Loading @@ -229,10 +229,14 @@ class AccountSettings( fun setSyncWiFiOnly(wiFiOnly: Boolean) = accountManager.setUserData(account, KEY_WIFI_ONLY, if (wiFiOnly) "1" else null) fun getSyncWifiOnlySSIDs(): List<String>? = (if (settings.containsKey(KEY_WIFI_ONLY_SSIDS)) fun getSyncWifiOnlySSIDs(): List<String>? = if (getSyncWifiOnly()) { (if (settings.containsKey(KEY_WIFI_ONLY_SSIDS)) settings.getString(KEY_WIFI_ONLY_SSIDS) else accountManager.getUserData(account, KEY_WIFI_ONLY_SSIDS))?.split(',') } else null fun setSyncWifiOnlySSIDs(ssids: List<String>?) = accountManager.setUserData(account, KEY_WIFI_ONLY_SSIDS, StringUtils.trimToNull(ssids?.joinToString(","))) Loading app/src/main/java/at/bitfire/davdroid/syncadapter/AccountUtils.kt +0 −1 Original line number Diff line number Diff line Loading @@ -5,7 +5,6 @@ import android.accounts.AccountManager import android.content.Context import android.os.Bundle import at.bitfire.davdroid.log.Logger import kotlin.jvm.Throws object AccountUtils { Loading app/src/main/java/at/bitfire/davdroid/syncadapter/SyncAdapterService.kt +12 −13 Original line number Diff line number Diff line Loading @@ -8,23 +8,19 @@ package at.bitfire.davdroid.syncadapter import android.Manifest import android.accounts.Account import android.app.Service import android.content.* import android.content.pm.PackageManager import android.net.ConnectivityManager import android.net.NetworkCapabilities import android.net.wifi.WifiManager import android.os.Build import android.os.Bundle import androidx.core.content.ContextCompat import androidx.core.content.getSystemService import at.bitfire.davdroid.InvalidAccountException import at.bitfire.davdroid.PermissionUtils import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.davdroid.ui.account.SettingsActivity import at.bitfire.davdroid.ui.account.WifiPermissionsActivity import java.lang.ref.WeakReference import java.util.* import java.util.logging.Level Loading Loading @@ -167,22 +163,25 @@ abstract class SyncAdapterService: Service() { // if execution reaches this point, we're on a connected WiFi settings.getSyncWifiOnlySSIDs()?.let { onlySSIDs -> // getting the WiFi name requires location permission (and active location services) since Android 8.1 // see https://issuetracker.google.com/issues/70633700 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 && ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { val intent = Intent(context, SettingsActivity::class.java) intent.putExtra(SettingsActivity.EXTRA_ACCOUNT, settings.account) // check required permissions and location status if (!PermissionUtils.canAccessWifiSsid(context)) { // not all permissions granted; show notification val intent = Intent(context, WifiPermissionsActivity::class.java) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) intent.putExtra(WifiPermissionsActivity.EXTRA_ACCOUNT, settings.account) PermissionUtils.notifyPermissions(context, intent) Logger.log.warning("Can't access WiFi SSID, aborting sync") return false } val wifi = context.getSystemService<WifiManager>()!! val info = wifi.connectionInfo if (info == null || !onlySSIDs.contains(info.ssid.trim('"'))) { Logger.log.info("Connected to wrong WiFi network (${info.ssid}), ignoring") Logger.log.info("Connected to wrong WiFi network (${info.ssid}), aborting sync") return false } } else Logger.log.fine("Connected to WiFi network ${info.ssid}") } } return true Loading Loading
app/src/main/AndroidManifest.xml +13 −5 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ <!-- getting the WiFi name (for "sync in Wifi only") requires - coarse location (Android 8.1) - fine location (Android 10) --> <uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION"/> <!-- required since Android 10 to get the WiFi name while in background (= while syncing) --> <uses-permission-sdk-23 android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/> Loading Loading @@ -88,11 +89,18 @@ android:name=".ui.account.AccountActivity" android:parentActivityName=".ui.AccountsActivity" android:theme="@style/AppTheme.NoActionBar"/> <activity android:name=".ui.account.SettingsActivity"/> <activity android:name=".ui.CreateAddressBookActivity" <activity android:name=".ui.CreateAddressBookActivity" android:label="@string/create_addressbook"/> <activity android:name=".ui.CreateCalendarActivity" <activity android:name=".ui.CreateCalendarActivity" android:label="@string/create_calendar"/> <activity android:name=".ui.account.SettingsActivity" /> <activity android:name=".ui.account.WifiPermissionsActivity" android:label="@string/wifi_permissions_label" android:parentActivityName=".ui.account.SettingsActivity" /> <activity android:name=".ui.DebugInfoActivity" Loading
app/src/main/java/at/bitfire/davdroid/PermissionUtils.kt +39 −0 Original line number Diff line number Diff line Loading @@ -5,9 +5,14 @@ import android.app.PendingIntent import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.location.LocationManager import android.net.Uri import android.os.Build import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.core.content.ContextCompat import androidx.core.location.LocationManagerCompat import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.ui.NotificationUtils import at.bitfire.davdroid.ui.PermissionsActivity Loading @@ -22,6 +27,31 @@ object PermissionUtils { Manifest.permission.WRITE_CALENDAR ) val WIFI_SSID_PERMISSIONS = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION) else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION) else arrayOf() /** * Checks whether all conditions to access the current WiFi's SSID are met: * * 1. location permissions ([WIFI_SSID_PERMISSIONS]) granted * 2. location enabled * * @return *true* if SSID can be obtained; *false* if the SSID will be <unknown> or something like that */ fun canAccessWifiSsid(context: Context): Boolean { val locationEnabled = ContextCompat.getSystemService(context, LocationManager::class.java)?.let { locationManager -> LocationManagerCompat.isLocationEnabled(locationManager) } ?: /* location feature not available on this device */ false return havePermissions(context, WIFI_SSID_PERMISSIONS) && locationEnabled } /** * Checks whether at least one of the given permissions is granted. * Loading Loading @@ -64,4 +94,13 @@ object PermissionUtils { .notify(NotificationUtils.NOTIFY_PERMISSIONS, notify) } fun showAppSettings(context: Context) { val intent = Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.fromParts("package", BuildConfig.APPLICATION_ID, null)) if (intent.resolveActivity(context.packageManager) != null) context.startActivity(intent) else Logger.log.warning("App settings Intent not resolvable") } } No newline at end of file
app/src/main/java/at/bitfire/davdroid/settings/AccountSettings.kt +8 −4 Original line number Diff line number Diff line Loading @@ -229,10 +229,14 @@ class AccountSettings( fun setSyncWiFiOnly(wiFiOnly: Boolean) = accountManager.setUserData(account, KEY_WIFI_ONLY, if (wiFiOnly) "1" else null) fun getSyncWifiOnlySSIDs(): List<String>? = (if (settings.containsKey(KEY_WIFI_ONLY_SSIDS)) fun getSyncWifiOnlySSIDs(): List<String>? = if (getSyncWifiOnly()) { (if (settings.containsKey(KEY_WIFI_ONLY_SSIDS)) settings.getString(KEY_WIFI_ONLY_SSIDS) else accountManager.getUserData(account, KEY_WIFI_ONLY_SSIDS))?.split(',') } else null fun setSyncWifiOnlySSIDs(ssids: List<String>?) = accountManager.setUserData(account, KEY_WIFI_ONLY_SSIDS, StringUtils.trimToNull(ssids?.joinToString(","))) Loading
app/src/main/java/at/bitfire/davdroid/syncadapter/AccountUtils.kt +0 −1 Original line number Diff line number Diff line Loading @@ -5,7 +5,6 @@ import android.accounts.AccountManager import android.content.Context import android.os.Bundle import at.bitfire.davdroid.log.Logger import kotlin.jvm.Throws object AccountUtils { Loading
app/src/main/java/at/bitfire/davdroid/syncadapter/SyncAdapterService.kt +12 −13 Original line number Diff line number Diff line Loading @@ -8,23 +8,19 @@ package at.bitfire.davdroid.syncadapter import android.Manifest import android.accounts.Account import android.app.Service import android.content.* import android.content.pm.PackageManager import android.net.ConnectivityManager import android.net.NetworkCapabilities import android.net.wifi.WifiManager import android.os.Build import android.os.Bundle import androidx.core.content.ContextCompat import androidx.core.content.getSystemService import at.bitfire.davdroid.InvalidAccountException import at.bitfire.davdroid.PermissionUtils import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.davdroid.ui.account.SettingsActivity import at.bitfire.davdroid.ui.account.WifiPermissionsActivity import java.lang.ref.WeakReference import java.util.* import java.util.logging.Level Loading Loading @@ -167,22 +163,25 @@ abstract class SyncAdapterService: Service() { // if execution reaches this point, we're on a connected WiFi settings.getSyncWifiOnlySSIDs()?.let { onlySSIDs -> // getting the WiFi name requires location permission (and active location services) since Android 8.1 // see https://issuetracker.google.com/issues/70633700 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 && ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { val intent = Intent(context, SettingsActivity::class.java) intent.putExtra(SettingsActivity.EXTRA_ACCOUNT, settings.account) // check required permissions and location status if (!PermissionUtils.canAccessWifiSsid(context)) { // not all permissions granted; show notification val intent = Intent(context, WifiPermissionsActivity::class.java) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) intent.putExtra(WifiPermissionsActivity.EXTRA_ACCOUNT, settings.account) PermissionUtils.notifyPermissions(context, intent) Logger.log.warning("Can't access WiFi SSID, aborting sync") return false } val wifi = context.getSystemService<WifiManager>()!! val info = wifi.connectionInfo if (info == null || !onlySSIDs.contains(info.ssid.trim('"'))) { Logger.log.info("Connected to wrong WiFi network (${info.ssid}), ignoring") Logger.log.info("Connected to wrong WiFi network (${info.ssid}), aborting sync") return false } } else Logger.log.fine("Connected to WiFi network ${info.ssid}") } } return true Loading