Better handling of the revocation of the "Storage" permission

* Check if permission is set when launching the app
* Show specific notification when auto upload fails due this state

Fixes #391 and fixes #397
This commit is contained in:
Julian Raufelder 2021-12-06 19:01:14 +01:00
parent c685cc3645
commit 07f93c0833
No known key found for this signature in database
GPG Key ID: 17EE71F6634E381D
4 changed files with 59 additions and 23 deletions

View File

@ -1,5 +1,6 @@
package org.cryptomator.presentation.presenter package org.cryptomator.presentation.presenter
import android.Manifest
import android.app.KeyguardManager import android.app.KeyguardManager
import android.app.admin.DevicePolicyManager import android.app.admin.DevicePolicyManager
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
@ -56,6 +57,7 @@ import org.cryptomator.presentation.workflow.ActivityResult
import org.cryptomator.presentation.workflow.AddExistingVaultWorkflow import org.cryptomator.presentation.workflow.AddExistingVaultWorkflow
import org.cryptomator.presentation.workflow.AuthenticationExceptionHandler import org.cryptomator.presentation.workflow.AuthenticationExceptionHandler
import org.cryptomator.presentation.workflow.CreateNewVaultWorkflow import org.cryptomator.presentation.workflow.CreateNewVaultWorkflow
import org.cryptomator.presentation.workflow.PermissionsResult
import org.cryptomator.presentation.workflow.Workflow import org.cryptomator.presentation.workflow.Workflow
import org.cryptomator.util.SharedPreferencesHandler import org.cryptomator.util.SharedPreferencesHandler
import javax.inject.Inject import javax.inject.Inject
@ -112,6 +114,10 @@ class VaultListPresenter @Inject constructor( //
} }
checkLicense() checkLicense()
if(sharedPreferencesHandler.usePhotoUpload()) {
checkLocalStoragePermissionRegardingAutoUpload()
}
} }
private fun checkLicense() { private fun checkLicense() {
@ -185,6 +191,27 @@ class VaultListPresenter @Inject constructor( //
} }
} }
private fun checkLocalStoragePermissionRegardingAutoUpload() {
requestPermissions(
PermissionsResultCallbacks.onLocalStoragePermissionGrantedForAutoUpload(), //
R.string.permission_snackbar_auth_auto_upload, //
Manifest.permission.READ_EXTERNAL_STORAGE
)
}
@Callback
fun onLocalStoragePermissionGrantedForAutoUpload(result: PermissionsResult) {
if (!result.granted()) {
Timber.tag("VaultListPresenter").e("Local storage permission not granted, auto upload will not work")
}
}
fun loadVaultList() {
view?.hideVaultCreationHint()
vaultList
assertUnlockingVaultIsLocked()
}
private fun assertUnlockingVaultIsLocked() { private fun assertUnlockingVaultIsLocked() {
if (view?.isShowingDialog(EnterPasswordDialog::class) == true) { if (view?.isShowingDialog(EnterPasswordDialog::class) == true) {
if (view?.currentDialog() != null) { if (view?.currentDialog() != null) {
@ -196,12 +223,6 @@ class VaultListPresenter @Inject constructor( //
} }
} }
fun loadVaultList() {
view?.hideVaultCreationHint()
vaultList
assertUnlockingVaultIsLocked()
}
fun deleteVault(vaultModel: VaultModel) { fun deleteVault(vaultModel: VaultModel) {
deleteVaultUseCase // deleteVaultUseCase //
.withVault(vaultModel.toVault()) // .withVault(vaultModel.toVault()) //

View File

@ -47,18 +47,15 @@ class AutoUploadNotification(private val context: Context, private val amountOfP
} }
private fun cancelNowAction(): NotificationCompat.Action { private fun cancelNowAction(): NotificationCompat.Action {
val intentAction = cancelAutoUploadIntent(context)
val cancelIntent = PendingIntent.getService(context, 0, intentAction, FLAG_CANCEL_CURRENT)
return NotificationCompat.Action.Builder( // return NotificationCompat.Action.Builder( //
R.drawable.ic_lock, // R.drawable.ic_lock, //
getString(R.string.notification_cancel_auto_upload), // getString(R.string.notification_cancel_auto_upload), //
cancelNowIntent() // cancelIntent //
).build() ).build()
} }
private fun cancelNowIntent(): PendingIntent {
val intentAction = cancelAutoUploadIntent(context)
return PendingIntent.getService(context, 0, intentAction, FLAG_CANCEL_CURRENT)
}
private fun startTheActivity(): PendingIntent { private fun startTheActivity(): PendingIntent {
val startTheActivity = Intent(context, VaultListActivity::class.java) val startTheActivity = Intent(context, VaultListActivity::class.java)
startTheActivity.action = ACTION_MAIN startTheActivity.action = ACTION_MAIN
@ -67,8 +64,8 @@ class AutoUploadNotification(private val context: Context, private val amountOfP
} }
fun update(progress: Int) { fun update(progress: Int) {
builder.setContentIntent(startTheActivity())
builder // builder //
.setContentIntent(startTheActivity())
.setContentText( // .setContentText( //
String.format( String.format(
context.getString(R.string.notification_auto_upload_message), // context.getString(R.string.notification_auto_upload_message), //
@ -105,27 +102,32 @@ class AutoUploadNotification(private val context: Context, private val amountOfP
showErrorWithMessage(context.getString(R.string.notification_auto_upload_failed_due_to_vault_not_found)) showErrorWithMessage(context.getString(R.string.notification_auto_upload_failed_due_to_vault_not_found))
} }
fun showPermissionNotGrantedNotification() {
Timber.tag("AutoUploadNotification").i("Show storage permission required notification")
showErrorWithMessage(context.getString(R.string.notification_auto_upload_permission_not_granted))
}
private fun showErrorWithMessage(message: String) { private fun showErrorWithMessage(message: String) {
builder.setContentIntent(startTheActivity()) builder
builder // .setContentIntent(startTheActivity())
.setContentTitle(context.getString(R.string.notification_auto_upload_failed_title)) .setContentTitle(context.getString(R.string.notification_auto_upload_failed_title))
.setContentText(message) // .setContentText(message) //
.setProgress(0, 0, false) .setProgress(0, 0, false)
.setAutoCancel(true) .setAutoCancel(true)
.setOngoing(false) .setOngoing(false)
.mActions.clear() .clearActions()
show() show()
} }
fun showUploadFinished(size: Int) { fun showUploadFinished(size: Int) {
builder.setContentIntent(startTheActivity()) builder
builder // .setContentIntent(startTheActivity())
.setContentTitle(context.getString(R.string.notification_auto_upload_finished_title)) .setContentTitle(context.getString(R.string.notification_auto_upload_finished_title))
.setContentText(format(context.getString(R.string.notification_auto_upload_finished_message), size)) // .setContentText(format(context.getString(R.string.notification_auto_upload_finished_message), size))
.setProgress(0, 0, false) .setProgress(0, 0, false)
.setAutoCancel(true) .setAutoCancel(true)
.setOngoing(false) .setOngoing(false)
.mActions.clear() .clearActions()
show() show()
} }

View File

@ -10,6 +10,8 @@ import android.net.Uri;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.Looper; import android.os.Looper;
import android.system.ErrnoException;
import android.system.OsConstants;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -37,6 +39,7 @@ import org.cryptomator.presentation.util.FileUtil;
import org.cryptomator.util.SharedPreferencesHandler; import org.cryptomator.util.SharedPreferencesHandler;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
@ -111,6 +114,8 @@ public class AutoUploadService extends Service {
notification.showVaultLockedDuringUpload(); notification.showVaultLockedDuringUpload();
} else if (e instanceof CancellationException) { } else if (e instanceof CancellationException) {
Timber.tag("AutoUploadService").i("Upload canceled by user"); Timber.tag("AutoUploadService").i("Upload canceled by user");
} else if (wrappedStoragePermissionException(e)) {
notification.showPermissionNotGrantedNotification();
} else { } else {
notification.showGeneralErrorDuringUpload(); notification.showGeneralErrorDuringUpload();
} }
@ -122,6 +127,14 @@ public class AutoUploadService extends Service {
worker.start(); worker.start();
} }
private boolean wrappedStoragePermissionException(Exception e) {
return e.getCause() != null //
&& e.getCause() instanceof FileNotFoundException //
&& e.getCause().getCause() != null //
&& e.getCause().getCause() instanceof ErrnoException //
&& ((ErrnoException) e.getCause().getCause()).errno == OsConstants.EACCES;
}
private void updateNotification(int asPercentage) { private void updateNotification(int asPercentage) {
if (elapsedTimeAutoUploadNotificationDelay > 200 && !cancelled) { if (elapsedTimeAutoUploadNotificationDelay > 200 && !cancelled) {
new Handler(Looper.getMainLooper()).post(() -> { new Handler(Looper.getMainLooper()).post(() -> {
@ -170,8 +183,7 @@ public class AutoUploadService extends Service {
} catch (FileRemovedDuringUploadException e) { } catch (FileRemovedDuringUploadException e) {
Timber.tag("AutoUploadService").i("Not uploading file because it was removed during upload"); Timber.tag("AutoUploadService").i("Not uploading file because it was removed during upload");
Timber.tag("AutoUploadService").v(format("Not uploading file because it was removed during upload %s", file.getFileName())); Timber.tag("AutoUploadService").v(format("Not uploading file because it was removed during upload %s", file.getFileName()));
} } catch (Exception e) {
catch (Exception e) {
cancelled = true; cancelled = true;
fileUtil.removeImagesFromAutoUploads(uploadedCloudFileNames); fileUtil.removeImagesFromAutoUploads(uploadedCloudFileNames);
throw e; throw e;
@ -200,7 +212,7 @@ public class AutoUploadService extends Service {
private CloudFile writeCloudFile(String fileName, CancelAwareDataSource dataSource, boolean replacing, ProgressAware<UploadState> progressAware) throws BackendException { private CloudFile writeCloudFile(String fileName, CancelAwareDataSource dataSource, boolean replacing, ProgressAware<UploadState> progressAware) throws BackendException {
Long size = dataSource.size(context); Long size = dataSource.size(context);
if(size == null) { if (size == null) {
throw new FileRemovedDuringUploadException(); throw new FileRemovedDuringUploadException();
} }
CloudFile source = cloudContentRepository.file(parent, fileName, size); CloudFile source = cloudContentRepository.file(parent, fileName, size);

View File

@ -538,6 +538,7 @@
<string name="notification_auto_upload_failed_due_to_folder_not_exists">Selected folder for upload isn\'t available anymore. Go to settings and choose a new one</string> <string name="notification_auto_upload_failed_due_to_folder_not_exists">Selected folder for upload isn\'t available anymore. Go to settings and choose a new one</string>
<string name="notification_auto_upload_failed_due_to_vault_locked">Vault locked during upload, please reopen vault to continue</string> <string name="notification_auto_upload_failed_due_to_vault_locked">Vault locked during upload, please reopen vault to continue</string>
<string name="notification_auto_upload_failed_due_to_vault_not_found">Vault specified for auto upload doesn\'t exist anymore.</string> <string name="notification_auto_upload_failed_due_to_vault_not_found">Vault specified for auto upload doesn\'t exist anymore.</string>
<string name="notification_auto_upload_permission_not_granted" translatable="false">@string/permission_snackbar_auth_auto_upload</string>
<string name="notification_cancel_open_writable_file" translatable="false">@string/dialog_button_cancel</string> <string name="notification_cancel_open_writable_file" translatable="false">@string/dialog_button_cancel</string>
<string name="notification_open_writable_file_title">Open writable file</string> <string name="notification_open_writable_file_title">Open writable file</string>