Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support SDcard read/write using all files access on Android 11+ (fixes #568) #618

Merged
merged 57 commits into from
Jul 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
3b3d51b
Update build.gradle
Catfriend1 Mar 15, 2020
f083af9
Update build.gradle
Catfriend1 Mar 15, 2020
3459998
Update gradle-wrapper.properties
Catfriend1 Mar 15, 2020
048e0d0
to-revert: Remove play publisher
Catfriend1 Mar 15, 2020
9c326d9
Update build.gradle
Catfriend1 Mar 15, 2020
32eebb7
Update build.gradle
Catfriend1 Mar 15, 2020
9b0a26d
Update build.gradle
Catfriend1 Mar 15, 2020
0d6d21c
Revert "to-revert: Remove play publisher"
Catfriend1 Mar 15, 2020
3220e40
Update build.gradle
Catfriend1 Mar 15, 2020
6686ec5
Revert "Update build.gradle"
Catfriend1 Mar 15, 2020
a4b9ba7
ToDo: work around missing GetVolumePath getPath on Android R
Catfriend1 Mar 15, 2020
343d2d4
Add android.permission.MANAGE_EXTERNAL_STORAGE
Catfriend1 Mar 15, 2020
e5ae349
Fix RtlCompat lint
Catfriend1 Mar 15, 2020
4e7f620
Merge branch 'master' into 20200315-android11
Catfriend1 Mar 15, 2020
67399f2
Merge branch 'master' into 20200315-android11
Catfriend1 Mar 16, 2020
ebe0f06
Update to NDK r21
Catfriend1 Mar 22, 2020
4e6e529
build-syncthing.py: Store prereq with version in filename
Catfriend1 Mar 22, 2020
d9d1256
Update SyncthingNative to v1.4.1-rc.3
Catfriend1 Mar 22, 2020
c373b90
build-syncthing.py: Store prereq with version in filename (2)
Catfriend1 Mar 22, 2020
963e976
build-syncthing: Update to Go 1.13.9
Catfriend1 Mar 22, 2020
4622dac
build.gradle: Update com.android.tools.build:gradle to 4.0.0-beta03
Catfriend1 Mar 22, 2020
7cfa4f7
build.gradle: Update buildToolsVersion to 30.0.0-rc2
Catfriend1 Mar 22, 2020
ef450d6
AndroidManifest.xml: requestLegacyExternalStorage="false" - turn scop…
Catfriend1 Mar 22, 2020
4c82de6
Merge branch 'master' into 20200315-android11
Catfriend1 Mar 22, 2020
c68abad
FileUtils: Treat volumeId "primary" as "/storage/emulated/0"
Catfriend1 Mar 22, 2020
2a3d401
Merge branch 'master' into 20200315-android11
Catfriend1 Mar 22, 2020
015bb66
Merge branch 'master' into 20200315-android11
Catfriend1 Apr 11, 2020
c19b6bf
Merge branch 'master' into 20200315-android11
Catfriend1 Apr 12, 2020
74ff09d
Merge branch 'master' into 20200315-android11
Catfriend1 Apr 30, 2020
bd90d77
Update build.gradle
Catfriend1 Apr 30, 2020
132b7bf
activity_first_start.xml: dirty fix - review necessary
Catfriend1 Apr 30, 2020
be0d330
values/dimens: Remove "dots_height", use "dots_margin_bottom" instead
Catfriend1 Apr 30, 2020
889282d
values/dimens: dots_margin_bottom 20dp > 45dp
Catfriend1 Apr 30, 2020
9f26760
activity_first_start.xml: dirty fix 2 - review necessary
Catfriend1 Apr 30, 2020
ebdc78d
indent layouts
Catfriend1 Apr 30, 2020
13b131f
Android R - REQUEST_FINE_LOCATION (#640=
Catfriend1 May 1, 2020
449efdb
Merge branch 'master' into 20200315-android11
Catfriend1 May 1, 2020
aaf610c
build.gradle: Update "com.github.triplet.play" from 2.6.2 to 2.7.5
Catfriend1 May 1, 2020
6651ca2
Merge branch 'master' into 20200315-android11
Catfriend1 May 1, 2020
86d5bc9
Revert "indent layouts"
Catfriend1 May 1, 2020
5b11b9b
Revert "activity_first_start.xml: dirty fix 2 - review necessary"
Catfriend1 May 1, 2020
c056f7b
Revert "values/dimens: dots_margin_bottom 20dp > 45dp"
Catfriend1 May 1, 2020
b9c9534
Revert "values/dimens: Remove "dots_height", use "dots_margin_bottom"…
Catfriend1 May 1, 2020
3425b51
Revert "activity_first_start.xml: dirty fix - review necessary"
Catfriend1 May 1, 2020
5d74dc0
FirstStartActivity: setSystemUiVisibility is deprecated
Catfriend1 May 1, 2020
29de730
Merge layouts from PR #646
Catfriend1 May 1, 2020
b4eed12
FirstStartActivity: setSystemUiVisibility is deprecated (2)
Catfriend1 May 1, 2020
0d17db1
Merge branch 'master' into 20200315-android11
Catfriend1 May 1, 2020
1de56de
Merge branch 'master' into 20200315-android11
Catfriend1 Jul 7, 2020
a3daa27
sdk bump 30.0
Catfriend1 Jul 7, 2020
5b72094
Update build.gradle
Catfriend1 Jul 7, 2020
eaf54b2
Update strings.xml
Catfriend1 Jul 7, 2020
071eb93
Update build.gradle
Catfriend1 Jul 7, 2020
974f686
Update FirstStartActivity.java
Catfriend1 Jul 7, 2020
6a2315e
Update FirstStartActivity.java
Catfriend1 Jul 7, 2020
8e4e2e2
Merge branch 'master' into 20200315-android11
Catfriend1 Jul 7, 2020
cbed43d
Update beta.txt
Catfriend1 Jul 7, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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