From 545a9ffb1dd419d662ebc280eb05bd9352a56a56 Mon Sep 17 00:00:00 2001 From: Catfriend1 Date: Wed, 8 Jul 2020 00:42:50 +0200 Subject: [PATCH] Support SDcard read/write using all files access on Android 11+ (fixes #568) (#618) * Update build.gradle * Update build.gradle * Update gradle-wrapper.properties * to-revert: Remove play publisher * Update build.gradle * Update build.gradle * Update build.gradle * Revert "to-revert: Remove play publisher" This reverts commit 048e0d028cc51cbd947f7406994893c4ee38e09f. * Update build.gradle * Revert "Update build.gradle" This reverts commit 3220e404dc6c5a543378caecc487488d358d4b8a. * ToDo: work around missing GetVolumePath getPath on Android R * Add android.permission.MANAGE_EXTERNAL_STORAGE * Fix RtlCompat lint fragment_staggered_versioning.xml:17: Error: To support older versions than API 17 (project specifies 16) you must also specify gravity or layout_gravity="center_horizontal" [RtlCompat] android:textAlignment="center" * Update to NDK r21 * build-syncthing.py: Store prereq with version in filename - go - ndk * Update SyncthingNative to v1.4.1-rc.3 * build-syncthing.py: Store prereq with version in filename (2) * build-syncthing: Update to Go 1.13.9 * build.gradle: Update com.android.tools.build:gradle to 4.0.0-beta03 * build.gradle: Update buildToolsVersion to 30.0.0-rc2 * AndroidManifest.xml: requestLegacyExternalStorage="false" - turn scoped storage on * FileUtils: Treat volumeId "primary" as "/storage/emulated/0" * Update build.gradle * activity_first_start.xml: dirty fix - review necessary * values/dimens: Remove "dots_height", use "dots_margin_bottom" instead * values/dimens: dots_margin_bottom 20dp > 45dp * activity_first_start.xml: dirty fix 2 - review necessary * indent layouts * Android R - REQUEST_FINE_LOCATION (#640= * build.gradle: Update "com.github.triplet.play" from 2.6.2 to 2.7.5 * Revert "indent layouts" This reverts commit ebdc78d1d63b41d1f53b172a5199d21ad8ff89a2. * Revert "activity_first_start.xml: dirty fix 2 - review necessary" This reverts commit 9f26760e262ba585c0d245bc7ba930a3aa9e2429. * Revert "values/dimens: dots_margin_bottom 20dp > 45dp" This reverts commit 889282de71b9344a41f6d2e992f96a869c55faf3. * Revert "values/dimens: Remove "dots_height", use "dots_margin_bottom" instead" This reverts commit be0d330034819131802226798ae16f9a3492653c. * Revert "activity_first_start.xml: dirty fix - review necessary" This reverts commit 132b7bf4a3a3c0b1599565dcdb7a19533eab8e4e. * FirstStartActivity: setSystemUiVisibility is deprecated - https://developer.android.com/reference/android/view/WindowInsets#getInsetsIgnoringVisibility(int) - https://developer.android.com/reference/android/view/View#SYSTEM_UI_FLAG_LAYOUT_STABLE * Merge layouts from PR #646 * FirstStartActivity: setSystemUiVisibility is deprecated (2) * sdk bump 30.0 * Update build.gradle * Update strings.xml * Update build.gradle * Update FirstStartActivity.java * Update FirstStartActivity.java * Update beta.txt Co-authored-by: Catfriend1 --- app/build.gradle | 9 +- app/src/main/AndroidManifest.xml | 2 + .../activities/FirstStartActivity.java | 92 ++++++++++++++++++- .../main/play/release-notes/en-GB/beta.txt | 2 + app/src/main/res/values/strings.xml | 1 + 5 files changed, 100 insertions(+), 6 deletions(-) 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