diff --git a/app/src/gplay/java/com/owncloud/android/services/firebase/NCFirebaseMessagingService.java b/app/src/gplay/java/com/owncloud/android/services/firebase/NCFirebaseMessagingService.java index d1bc2712b7..ce6beea9b7 100644 --- a/app/src/gplay/java/com/owncloud/android/services/firebase/NCFirebaseMessagingService.java +++ b/app/src/gplay/java/com/owncloud/android/services/firebase/NCFirebaseMessagingService.java @@ -7,8 +7,10 @@ */ package com.owncloud.android.services.firebase; +import android.content.Intent; import android.text.TextUtils; +import com.google.firebase.messaging.Constants.MessageNotificationKeys; import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.RemoteMessage; import com.nextcloud.client.account.UserAccountManager; @@ -16,6 +18,7 @@ import com.nextcloud.client.jobs.BackgroundJobManager; import com.nextcloud.client.jobs.NotificationWork; import com.nextcloud.client.preferences.AppPreferences; import com.owncloud.android.R; +import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.utils.PushUtils; import java.util.Map; @@ -30,14 +33,55 @@ public class NCFirebaseMessagingService extends FirebaseMessagingService { @Inject UserAccountManager accountManager; @Inject BackgroundJobManager backgroundJobManager; + static final String TAG = "NCFirebaseMessagingService"; + + // Firebase Messaging may apparently use two intent extras to specify a notification message. + // + // See the following fragments in https://github.com/firebase/firebase-android-sdk/blob/releases/m144_1.release/ + // firebase-messaging/src/main/java/com/google/firebase/messaging/FirebaseMessagingService.java#L223 + // firebase-messaging/src/main/java/com/google/firebase/messaging/NotificationParams.java#L419 + // firebase-messaging/src/main/java/com/google/firebase/messaging/Constants.java#L158 + // + // The "old" key is not exposed in com.google.firebase.messaging.Constants.MessageNotificationKeys, + // so we need to define it ourselves. + static final String ENABLE_NOTIFICATION_OLD = MessageNotificationKeys.NOTIFICATION_PREFIX_OLD + "e"; + static final String ENABLE_NOTIFICATION_NEW = MessageNotificationKeys.ENABLE_NOTIFICATION; + @Override public void onCreate() { super.onCreate(); AndroidInjection.inject(this); } + @Override + public void handleIntent(Intent intent) { + Log_OC.d(TAG, "handleIntent - extras: " + + ENABLE_NOTIFICATION_NEW + ": " + intent.getExtras().getString(ENABLE_NOTIFICATION_NEW) + ", " + + ENABLE_NOTIFICATION_OLD + ": " + intent.getExtras().getString(ENABLE_NOTIFICATION_OLD)); + + // When the app is in background and one of the ENABLE_NOTIFICATION or ENABLE_NOTIFICATION_OLD extras is set + // to "1" in the intent sent from the FCM system code to the FirebaseMessagingService in the application, + // the FCM library code that handles the intent DOES NOT invoke the onMessageReceived method. + // It just displays the notification by itself. + // + // In our case the original FCM message contains dummy values "NEW_NOTIFICATION" and we need to get the + // message in onMessageReceived to decrypt it. + // + // So we cheat here a little, by telling the FCM library that the notification flag is not set. + // + // Code below depends on implementation details of the firebase-messaging library (Firebase Android SDK). + // https://github.com/firebase/firebase-android-sdk/tree/master/firebase-messaging + + intent.removeExtra(ENABLE_NOTIFICATION_OLD); + intent.removeExtra(ENABLE_NOTIFICATION_NEW); + intent.putExtra(ENABLE_NOTIFICATION_NEW, "0"); + + super.handleIntent(intent); + } + @Override public void onMessageReceived(@NonNull RemoteMessage remoteMessage) { + Log_OC.d(TAG, "onMessageReceived"); final Map data = remoteMessage.getData(); final String subject = data.get(NotificationWork.KEY_NOTIFICATION_SUBJECT); final String signature = data.get(NotificationWork.KEY_NOTIFICATION_SIGNATURE); @@ -48,6 +92,7 @@ public class NCFirebaseMessagingService extends FirebaseMessagingService { @Override public void onNewToken(@NonNull String newToken) { + Log_OC.d(TAG, "onNewToken"); super.onNewToken(newToken); if (!TextUtils.isEmpty(getResources().getString(R.string.push_server_url))) { diff --git a/app/src/main/java/com/nextcloud/client/jobs/InternalTwoWaySyncWork.kt b/app/src/main/java/com/nextcloud/client/jobs/InternalTwoWaySyncWork.kt index fda84ce361..1d1c89fd9d 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/InternalTwoWaySyncWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/InternalTwoWaySyncWork.kt @@ -32,6 +32,7 @@ class InternalTwoWaySyncWork( private val appPreferences: AppPreferences ) : Worker(context, params) { private var shouldRun = true + private var operation: SynchronizeFolderOperation? = null override fun doWork(): Result { Log_OC.d(TAG, "Worker started!") @@ -66,10 +67,10 @@ class InternalTwoWaySyncWork( } Log_OC.d(TAG, "Folder ${folder.remotePath}: started!") - val operation = SynchronizeFolderOperation(context, folder.remotePath, user, fileDataStorageManager) - .execute(context) + operation = SynchronizeFolderOperation(context, folder.remotePath, user, fileDataStorageManager, true) + val operationResult = operation?.execute(context) - if (operation.isSuccess) { + if (operationResult?.isSuccess == true) { Log_OC.d(TAG, "Folder ${folder.remotePath}: finished!") } else { Log_OC.d(TAG, "Folder ${folder.remotePath} failed!") @@ -77,7 +78,10 @@ class InternalTwoWaySyncWork( } folder.apply { - internalFolderSyncResult = operation.code.toString() + operationResult?.let { + internalFolderSyncResult = it.code.toString() + } + internalFolderSyncTimestamp = System.currentTimeMillis() } @@ -96,6 +100,7 @@ class InternalTwoWaySyncWork( override fun onStopped() { Log_OC.d(TAG, "OnStopped of worker called!") + operation?.cancel() shouldRun = false super.onStopped() } diff --git a/app/src/main/java/com/nextcloud/client/jobs/OfflineSyncWork.kt b/app/src/main/java/com/nextcloud/client/jobs/OfflineSyncWork.kt index 84663a9786..99f6481cf9 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/OfflineSyncWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/OfflineSyncWork.kt @@ -77,7 +77,8 @@ class OfflineSyncWork constructor( user, true, context, - storageManager + storageManager, + true ) synchronizeFileOperation.execute(context) } diff --git a/app/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java b/app/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java index 6caf81f471..7c66d1128f 100644 --- a/app/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java @@ -46,6 +46,8 @@ public class SynchronizeFileOperation extends SyncOperation { private boolean mSyncFileContents; private Context mContext; private boolean mTransferWasRequested; + private final boolean syncInBackgroundWorker; + /** * When 'false', uploads to the server are not done; only downloads or conflict detection. This is a temporal @@ -74,7 +76,8 @@ public class SynchronizeFileOperation extends SyncOperation { User user, boolean syncFileContents, Context context, - FileDataStorageManager storageManager) { + FileDataStorageManager storageManager, + boolean syncInBackgroundWorker) { super(storageManager); mRemotePath = remotePath; @@ -84,6 +87,7 @@ public class SynchronizeFileOperation extends SyncOperation { mSyncFileContents = syncFileContents; mContext = context; mAllowUploads = true; + this.syncInBackgroundWorker = syncInBackgroundWorker; } @@ -110,7 +114,8 @@ public class SynchronizeFileOperation extends SyncOperation { User user, boolean syncFileContents, Context context, - FileDataStorageManager storageManager) { + FileDataStorageManager storageManager, + boolean syncInBackgroundWorker) { super(storageManager); mLocalFile = localFile; @@ -130,6 +135,7 @@ public class SynchronizeFileOperation extends SyncOperation { mSyncFileContents = syncFileContents; mContext = context; mAllowUploads = true; + this.syncInBackgroundWorker = syncInBackgroundWorker; } @@ -159,9 +165,9 @@ public class SynchronizeFileOperation extends SyncOperation { boolean syncFileContents, boolean allowUploads, Context context, - FileDataStorageManager storageManager) { - - this(localFile, serverFile, user, syncFileContents, context, storageManager); + FileDataStorageManager storageManager, + boolean syncInBackgroundWorker) { + this(localFile, serverFile, user, syncFileContents, context, storageManager, syncInBackgroundWorker); mAllowUploads = allowUploads; } @@ -295,13 +301,29 @@ public class SynchronizeFileOperation extends SyncOperation { } private void requestForDownload(OCFile file) { - Log_OC.d("InternalTwoWaySyncWork", "download file: " + file.getFileName()); - - FileDownloadHelper.Companion.instance().downloadFile( - mUser, - file); + if (syncInBackgroundWorker) { + Log_OC.d("InternalTwoWaySyncWork", "download file: " + file.getFileName()); - mTransferWasRequested = true; + try { + final var operation = new DownloadFileOperation(mUser, file, mContext); + var result = operation.execute(getClient()); + + mTransferWasRequested = true; + + String filename = file.getFileName(); + if (filename != null) { + if (result.isSuccess()) { + Log_OC.d(TAG, "requestForDownload completed for: " + file.getFileName()); + } else { + Log_OC.d(TAG, "requestForDownload failed for: " + file.getFileName()); + } + } + } catch (Exception e) { + Log_OC.d(TAG, "Exception caught at requestForDownload" + e); + } + } else { + FileDownloadHelper.Companion.instance().downloadFile(mUser, file); + } } public boolean transferWasRequested() { diff --git a/app/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java b/app/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java index c4e4bf05c0..1879e8b042 100644 --- a/app/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java @@ -40,7 +40,6 @@ import java.util.List; import java.util.Map; import java.util.Vector; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @@ -87,6 +86,8 @@ public class SynchronizeFolderOperation extends SyncOperation { private final AtomicBoolean mCancellationRequested; + private final boolean syncInBackgroundWorker; + /** * Creates a new instance of {@link SynchronizeFolderOperation}. * @@ -97,7 +98,8 @@ public class SynchronizeFolderOperation extends SyncOperation { public SynchronizeFolderOperation(Context context, String remotePath, User user, - FileDataStorageManager storageManager) { + FileDataStorageManager storageManager, + boolean syncInBackgroundWorker) { super(storageManager); mRemotePath = remotePath; @@ -107,6 +109,7 @@ public class SynchronizeFolderOperation extends SyncOperation { mFilesForDirectDownload = new Vector<>(); mFilesToSyncContents = new Vector<>(); mCancellationRequested = new AtomicBoolean(false); + this.syncInBackgroundWorker = syncInBackgroundWorker; } @@ -406,7 +409,8 @@ public class SynchronizeFolderOperation extends SyncOperation { user, true, mContext, - getStorageManager() + getStorageManager(), + syncInBackgroundWorker ); mFilesToSyncContents.add(operation); } @@ -427,7 +431,8 @@ public class SynchronizeFolderOperation extends SyncOperation { user, true, mContext, - getStorageManager() + getStorageManager(), + syncInBackgroundWorker ); mFilesToSyncContents.add(operation); } @@ -441,8 +446,40 @@ public class SynchronizeFolderOperation extends SyncOperation { } private void startDirectDownloads() { - final var fileDownloadHelper = FileDownloadHelper.Companion.instance(); - mFilesForDirectDownload.forEach(file -> fileDownloadHelper.downloadFile(user, file)); + if (syncInBackgroundWorker) { + try { + for (OCFile file: mFilesForDirectDownload) { + synchronized (mCancellationRequested) { + if (mCancellationRequested.get()) { + break; + } + } + + if (file == null) { + continue; + } + + final var operation = new DownloadFileOperation(user, file, mContext); + var result = operation.execute(getClient()); + + String filename = file.getFileName(); + if (filename == null) { + continue; + } + + if (result.isSuccess()) { + Log_OC.d(TAG, "startDirectDownloads completed for: " + file.getFileName()); + } else { + Log_OC.d(TAG, "startDirectDownloads failed for: " + file.getFileName()); + } + } + } catch (Exception e) { + Log_OC.d(TAG, "Exception caught at startDirectDownloads" + e); + } + } else { + final var fileDownloadHelper = FileDownloadHelper.Companion.instance(); + mFilesForDirectDownload.forEach(file -> fileDownloadHelper.downloadFile(user, file)); + } } /** diff --git a/app/src/main/java/com/owncloud/android/services/OperationsService.java b/app/src/main/java/com/owncloud/android/services/OperationsService.java index 648c7ce1b4..0cd2d5e30a 100644 --- a/app/src/main/java/com/owncloud/android/services/OperationsService.java +++ b/app/src/main/java/com/owncloud/android/services/OperationsService.java @@ -698,7 +698,8 @@ public class OperationsService extends Service { user, syncFileContents, getApplicationContext(), - fileDataStorageManager); + fileDataStorageManager, + false); break; case ACTION_SYNC_FOLDER: @@ -707,7 +708,8 @@ public class OperationsService extends Service { this, // TODO remove this dependency from construction time remotePath, user, - fileDataStorageManager + fileDataStorageManager, + false ); break; diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index b7b9b2dc14..aeb3766fd5 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -68,6 +68,7 @@ import com.nextcloud.utils.extensions.ActivityExtensionsKt; import com.nextcloud.utils.extensions.BundleExtensionsKt; import com.nextcloud.utils.extensions.FileExtensionsKt; import com.nextcloud.utils.extensions.IntentExtensionsKt; +import com.nextcloud.utils.fileNameValidator.FileNameValidator; import com.nextcloud.utils.view.FastScrollUtils; import com.owncloud.android.MainApp; import com.owncloud.android.R; @@ -952,6 +953,12 @@ public class FileDisplayActivity extends FileActivity connectivityService.isNetworkAndServerAvailable(result -> { if (result) { + boolean isValidFolderPath = FileNameValidator.INSTANCE.checkFolderPath(remotePathBase,getCapabilities(),this); + if (!isValidFolderPath) { + DisplayUtils.showSnackMessage(this, R.string.file_name_validator_error_contains_reserved_names_or_invalid_characters); + return; + } + FileUploadHelper.Companion.instance().uploadNewFiles(getUser().orElseThrow(RuntimeException::new), filePaths, decryptedRemotePaths, diff --git a/app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java index 6ffc78b23c..9b7514bd01 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java @@ -35,7 +35,6 @@ import com.nextcloud.client.jobs.upload.FileUploadWorker; import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.utils.extensions.ActivityExtensionsKt; import com.nextcloud.utils.extensions.FileExtensionsKt; -import com.nextcloud.utils.fileNameValidator.FileNameValidator; import com.owncloud.android.R; import com.owncloud.android.databinding.UploadFilesLayoutBinding; import com.owncloud.android.lib.common.utils.Log_OC; @@ -60,7 +59,6 @@ import javax.inject.Inject; import androidx.activity.OnBackPressedCallback; import androidx.annotation.NonNull; -import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.ActionBar; import androidx.appcompat.widget.SearchView; import androidx.core.view.MenuItemCompat; diff --git a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java index 25a5e4a739..6b765c2afd 100755 --- a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java +++ b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java @@ -225,7 +225,8 @@ public class FileOperationsHelper { user, true, fileActivity, - storageManager); + storageManager, + false); RemoteOperationResult result = sfo.execute(fileActivity); if (result.getCode() == RemoteOperationResult.ResultCode.SYNC_CONFLICT) { @@ -303,7 +304,8 @@ public class FileOperationsHelper { user, true, fileActivity, - storageManager); + storageManager, + false); RemoteOperationResult result = sfo.execute(fileActivity); fileActivity.dismissLoadingDialog(); if (result.getCode() == RemoteOperationResult.ResultCode.SYNC_CONFLICT) {