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