Skip to content

Commit

Permalink
Support SDcard read/write using all files access on Android 11+ (fixes
Browse files Browse the repository at this point in the history
…#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 048e0d0.

* Update build.gradle

* Revert "Update build.gradle"

This reverts commit 3220e40.

* 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 ebdc78d.

* Revert "activity_first_start.xml: dirty fix 2 - review necessary"

This reverts commit 9f26760.

* Revert "values/dimens: dots_margin_bottom 20dp > 45dp"

This reverts commit 889282d.

* Revert "values/dimens: Remove "dots_height", use "dots_margin_bottom" instead"

This reverts commit be0d330.

* Revert "activity_first_start.xml: dirty fix - review necessary"

This reverts commit 132b7bf.

* 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 <[email protected]>
  • Loading branch information
Catfriend1 and Catfriend1 authored Jul 7, 2020
1 parent ea924e8 commit 545a9ff
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 6 deletions.
9 changes: 5 additions & 4 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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 {
Expand All @@ -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'
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<!-- CAMERA is required for the "Syncthing Camera" feature -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- MANAGE_EXTERNAL_STORAGE is required on Android 11 "R" -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

<application
android:allowBackup="false"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.PowerManager;
import android.provider.Settings;
import androidx.annotation.NonNull;
Expand Down Expand Up @@ -50,6 +51,7 @@ public class FirstStartActivity extends AppCompatActivity {
private static String TAG = "FirstStartActivity";
private static final int REQUEST_COARSE_LOCATION = 141;
private static final int REQUEST_BACKGROUND_LOCATION = 142;
private static final int REQUEST_FINE_LOCATION = 144;
private static final int REQUEST_WRITE_STORAGE = 143;

private static class Slide {
Expand Down Expand Up @@ -104,6 +106,9 @@ protected void onCreate(Bundle savedInstanceState) {
* If anything mandatory is missing, the according welcome slide(s) will be shown.
*/
Boolean showSlideStoragePermission = !haveStoragePermission();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
showSlideStoragePermission = showSlideStoragePermission || !haveAllFilesAccessPermission();
}
Boolean showSlideIgnoreDozePermission = !haveIgnoreDozePermission();
Boolean showSlideLocationPermission = !haveLocationPermission();
Boolean showSlideKeyGeneration = !checkForParseableConfig();
Expand All @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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[]{
Expand Down Expand Up @@ -527,13 +593,35 @@ 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) {
Log.i(TAG, "User denied WRITE_EXTERNAL_STORAGE permission.");
} 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;
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/play/release-notes/en-GB/beta.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Please report any problems you encounter via Github.</string>
<!-- Slide "Storage Permission" -->
<string name="storage_permission_title">Storage Permission</string>
<string name="storage_permission_desc">Syncthing needs to access your storage to do file synchronization.</string>
<string name="dialog_all_files_access_not_supported">Your device does not support all files access</string>

<!-- Slide "Ignore battery optimizations" -->
<string name="ignore_doze_permission_title">Battery Optimization</string>
Expand Down

0 comments on commit 545a9ff

Please sign in to comment.