diff --git a/app/build.gradle b/app/build.gradle
index 4af08c621..b33d945ae 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -16,6 +16,7 @@ dependencies {
implementation 'com.annimon:stream:1.2.1'
implementation 'com.android.volley:volley:1.1.1'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+ implementation 'androidx.core:core:1.3.0'
implementation 'androidx.documentfile:documentfile:1.0.1'
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
implementation "androidx.preference:preference:1.1.1"
@@ -31,11 +32,11 @@ def ourVersionName = "${versionMajor}.${versionMinor}.${versionPatch}.${versionW
android {
// Changes to these values need to be reflected in `.travis.yml`
- compileSdkVersion 29
- buildToolsVersion '29.0.2'
+ compileSdkVersion 30
+ buildToolsVersion '30.0.0'
buildTypes.debug.applicationIdSuffix ".debug"
- dataBinding.enabled = true
+ buildFeatures.dataBinding = true
playConfigs {
defaultAccountConfig {
@@ -45,7 +46,7 @@ android {
defaultConfig {
applicationId "com.github.catfriend1.syncthingandroid"
minSdkVersion 16
- targetSdkVersion 29
+ targetSdkVersion 30
versionCode ourVersionCode
versionName ourVersionName
testApplicationId 'com.github.catfriend1.syncthingandroid.test'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3b80c3a0a..9aaefc336 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -29,6 +29,8 @@
+
+
= Build.VERSION_CODES.R) {
+ showSlideStoragePermission = showSlideStoragePermission || !haveAllFilesAccessPermission();
+ }
Boolean showSlideIgnoreDozePermission = !haveIgnoreDozePermission();
Boolean showSlideLocationPermission = !haveLocationPermission();
Boolean showSlideKeyGeneration = !checkForParseableConfig();
@@ -119,6 +124,20 @@ protected void onCreate(Bundle savedInstanceState) {
return;
}
+ // Log what's missing and preventing us from directly starting into MainActivity.
+ if (showSlideStoragePermission) {
+ Log.d(TAG, "We (no longer?) have storage permission and will politely ask for it.");
+ }
+ if (showSlideIgnoreDozePermission) {
+ Log.d(TAG, "We (no longer?) have ignore doze permission and will politely ask for it on phones.");
+ }
+ if (showSlideLocationPermission) {
+ Log.d(TAG, "We (no longer?) have location permission and will politely ask for it.");
+ }
+ if (showSlideKeyGeneration) {
+ Log.d(TAG, "We (no longer?) have a valid Syncthing config and will attempt to generate a fresh config.");
+ }
+
// Make notification bar transparent (API level 21+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
@@ -231,7 +250,11 @@ public void onBtnNextClick() {
// Check if we are allowed to advance to the next slide.
if (mViewPager.getCurrentItem() == mSlidePosStoragePermission) {
// As the storage permission is a prerequisite to run syncthing, refuse to continue without it.
- if (!haveStoragePermission()) {
+ Boolean storagePermissionsGranted = haveStoragePermission();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ storagePermissionsGranted = storagePermissionsGranted && haveAllFilesAccessPermission();
+ }
+ if (!storagePermissionsGranted) {
Toast.makeText(this, R.string.toast_write_storage_permission_required,
Toast.LENGTH_LONG).show();
return;
@@ -424,6 +447,40 @@ private void startApp() {
/**
* Permission check and request functions
*/
+ @TargetApi(30)
+ private boolean haveAllFilesAccessPermission() {
+ return Environment.isExternalStorageManager();
+ }
+
+ @TargetApi(30)
+ private void requestAllFilesAccessPermission() {
+ Boolean intentFailed = false;
+ Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
+ intent.setData(Uri.parse("package:" + getPackageName()));
+ try {
+ ComponentName componentName = intent.resolveActivity(getPackageManager());
+ if (componentName != null) {
+ String className = componentName.getClassName();
+ if (className != null) {
+ // Launch "Allow all files access?" dialog.
+ startActivity(intent);
+ return;
+ }
+ intentFailed = true;
+ } else {
+ Log.w(TAG, "Request all files access not supported");
+ intentFailed = true;
+ }
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "Request all files access not supported", e);
+ intentFailed = true;
+ }
+ if (intentFailed) {
+ // Some devices don't support this request.
+ Toast.makeText(this, R.string.dialog_all_files_access_not_supported, Toast.LENGTH_LONG).show();
+ }
+ }
+
private boolean haveIgnoreDozePermission() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
// Older android version don't have the doze feature so we'll assume having the anti-doze permission.
@@ -475,7 +532,16 @@ private boolean haveLocationPermission() {
}
private void requestLocationPermission() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ ActivityCompat.requestPermissions(
+ this,
+ new String[]{
+ Manifest.permission.ACCESS_FINE_LOCATION
+ },
+ REQUEST_FINE_LOCATION
+ );
+ return;
+ } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
ActivityCompat.requestPermissions(
this,
new String[]{
@@ -527,6 +593,25 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis
mNextButton.requestFocus();
}
break;
+ case REQUEST_FINE_LOCATION:
+ if (grantResults.length == 0 ||
+ grantResults[0] != PackageManager.PERMISSION_GRANTED) {
+ Log.i(TAG, "User denied ACCESS_FINE_LOCATION permission.");
+ return;
+ }
+ Toast.makeText(this, R.string.permission_granted, Toast.LENGTH_SHORT).show();
+ Log.i(TAG, "User granted ACCESS_FINE_LOCATION permission.");
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ ActivityCompat.requestPermissions(
+ this,
+ new String[]{
+ Manifest.permission.ACCESS_BACKGROUND_LOCATION
+ },
+ REQUEST_BACKGROUND_LOCATION
+ );
+ return;
+ }
+ break;
case REQUEST_WRITE_STORAGE:
if (grantResults.length == 0 ||
grantResults[0] != PackageManager.PERMISSION_GRANTED) {
@@ -534,6 +619,9 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis
} else {
Toast.makeText(this, R.string.permission_granted, Toast.LENGTH_SHORT).show();
Log.i(TAG, "User granted WRITE_EXTERNAL_STORAGE permission.");
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ requestAllFilesAccessPermission();
+ }
mNextButton.requestFocus();
}
break;
diff --git a/app/src/main/play/release-notes/en-GB/beta.txt b/app/src/main/play/release-notes/en-GB/beta.txt
index 1d3e4eddb..0d4b6a9dd 100644
--- a/app/src/main/play/release-notes/en-GB/beta.txt
+++ b/app/src/main/play/release-notes/en-GB/beta.txt
@@ -5,6 +5,8 @@ Fixed
Enhanced
--
+- Initial SDcard read/write support on Android 11+ (#618)
Other issues
--
+- Android 11 wrapper parts are use-able, but not complete yet.
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 6d96d3314..d469bf02b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -24,6 +24,7 @@ Please report any problems you encounter via Github.
Storage Permission
Syncthing needs to access your storage to do file synchronization.
+ Your device does not support all files access
Battery Optimization