Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import com.owncloud.android.lib.resources.files.model.RemoteFile
import com.owncloud.android.operations.CreateFolderOperation
import com.owncloud.android.operations.RemoveFileOperation
import com.owncloud.android.operations.RenameFileOperation
import com.owncloud.android.utils.FileStorageUtils
import com.owncloud.android.utils.MimeTypeUtil
import com.owncloud.android.utils.theme.ViewThemeUtils
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -67,8 +68,8 @@ class OfflineOperationsWorker(

// check network connection
if (!connectivityService.isNetworkAndServerAvailableSuspended()) {
Log_OC.w(TAG, "⚠️ No internet/server connection. Retrying later...")
return@withContext Result.retry()
Log_OC.w(TAG, "⚠️ No internet/server connection. Waiting for the next trigger...")
return@withContext Result.success()
}

// check offline operations
Expand Down Expand Up @@ -160,30 +161,53 @@ class OfflineOperationsWorker(
}
// endregion

private fun getExecutionPath(operation: OfflineOperationEntity): String? {
val path = operation.path ?: return null

return if (operation.type is OfflineOperationType.CreateFile) {
path.removeSuffix(OCFile.PATH_SEPARATOR)
} else {
path
}
}

private fun adoptRemoteFolder(operation: OfflineOperationEntity, remoteFile: RemoteFile, ocFile: OCFile?) {
ocFile?.let {
val adoptedFile = FileStorageUtils.fillOCFile(remoteFile).apply {
fileId = it.fileId
parentId = it.parentId
decryptedRemotePath = it.decryptedRemotePath
}

fileDataStorageManager.saveFile(adoptedFile)
}

repository.updateNextOperations(operation)
fileDataStorageManager.offlineOperationDao.delete(operation)
notificationManager.dismissNotification(operation.id)
}

// region Operation Execution
@Suppress("ComplexCondition", "LongMethod")
private suspend fun executeOperation(
operation: OfflineOperationEntity,
client: OwnCloudClient
): OfflineOperationResult? = withContext(Dispatchers.IO) {
var path = (operation.path)
val path = getExecutionPath(operation)
if (path == null) {
Log_OC.w(TAG, "⚠️ Skipped: path is null for operation id=${operation.id}")
return@withContext null
}

if (operation.type is OfflineOperationType.CreateFile && path.endsWith(OCFile.PATH_SEPARATOR)) {
Log_OC.w(
TAG,
"Create file operation should not ends with path separator removing suffix, " +
"operation id=${operation.id}"
)
path = path.removeSuffix(OCFile.PATH_SEPARATOR)
}

val remoteFile = getRemoteFile(path)
val ocFile = fileDataStorageManager.getFileByDecryptedRemotePath(path)

if (operation.type is OfflineOperationType.CreateFolder && remoteFile != null) {
Log_OC.d(TAG, "📂 Folder already exists on server, adopting it: $path")
adoptRemoteFolder(operation, remoteFile, ocFile)
return@withContext null
}

if (remoteFile != null && ocFile != null && isFileChanged(remoteFile, ocFile)) {
Log_OC.w(TAG, "⚠️ Conflict detected: File already exists on server. Skipping operation id=${operation.id}")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ class ConnectivityServiceImpl(

// region private values
private val scope = CoroutineScope(Dispatchers.IO)
private var availabilityCheckJob: Job? = null
private var notifyJob: Job? = null
private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
private val listeners = mutableSetOf<NetworkChangeListener>()
Expand Down Expand Up @@ -82,8 +81,7 @@ class ConnectivityServiceImpl(

// region overridden methods
override fun isNetworkAndServerAvailable(onCompleted: (Boolean) -> Unit) {
availabilityCheckJob?.cancel()
availabilityCheckJob = scope.launch {
scope.launch {
val available = !isInternetWalled()
Log_OC.d(TAG, "isNetworkAndServerAvailable: $available")
withContext(Dispatchers.Main) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import com.nextcloud.client.core.ClockImpl
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds

@Singleton
class WalledCheckCache @Inject constructor() {
Expand All @@ -29,16 +31,18 @@ class WalledCheckCache @Inject constructor() {
}

fun getValue(key: ConnectivityKey): Boolean? {
val entry = walledStatusCache[key] ?: return null
val isExpired = (clock.currentTime - entry.first) >= CACHE_TIME_MS
return if (isExpired) null else entry.second
val (checkedAt, isWalled) = walledStatusCache[key] ?: return null
val cacheTime = if (isWalled) WALLED_CACHE_TIME_MS else REACHABLE_CACHE_TIME_MS
val isExpired = (clock.currentTime - checkedAt) >= cacheTime
return if (isExpired) null else isWalled
}

fun putConnectivityValue(key: ConnectivityKey, connectivity: Connectivity) {
connectivityCache[key] = connectivity
}

companion object {
private const val CACHE_TIME_MS = 10 * 60 * 1000
private val REACHABLE_CACHE_TIME_MS = 10.minutes.inWholeMilliseconds
private val WALLED_CACHE_TIME_MS = 30.seconds.inWholeMilliseconds
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ fun FileActivity.removeFiles(
onlyLocalCopy: Boolean,
filesRemovedListener: OnFilesRemovedListener?
) {
if (files.isEmpty()) {
filesRemovedListener?.onFilesRemoved()
return
}

connectivityService.isNetworkAndServerAvailable { isAvailable ->
if (isAvailable) {
showLoadingDialog(getString(R.string.wait_a_moment))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ public void createPendingFile(String path, String mimeType, long createdAt, long
file.setMimeType(mimeType);
file.setCreationTimestamp(createdAt);
file.setModificationTimestamp(modificationTimestamp);
file.setPermissions(getParentPermissions(path));
saveFileWithParent(file, MainApp.getAppContext());
}

Expand All @@ -243,9 +244,21 @@ public void createPendingDirectory(String path, long createdAt, long modificatio
directory.setMimeType(MimeType.DIRECTORY);
directory.setCreationTimestamp(createdAt);
directory.setModificationTimestamp(modificationTimestamp);
directory.setPermissions(getParentPermissions(path));
saveFileWithParent(directory, MainApp.getAppContext());
}

@Nullable
private String getParentPermissions(String path) {
String parentPath = FileStorageUtils.getParentPath(path);
if (parentPath == null) {
return null;
}

OCFile parent = getFileByDecryptedRemotePath(parentPath);
return parent == null ? null : parent.getPermissions();
}

public void deleteOfflineOperation(OCFile file) {
offlineOperationsRepository.deleteOperation(file);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -516,20 +516,23 @@ private String createRandomFileName(DecryptedFolderMetadataFileV1 metadata) {
private RemoteOperationResult<?> normalCreate(OwnCloudClient client) {
final var result = new CreateFolderRemoteOperation(remotePath, true).execute(client);

if (result.isSuccess()) {
final var remoteFolderOperationResult = new ReadFolderRemoteOperation(remotePath)
.execute(client);
if (!result.isSuccess()) {
Log_OC.e(TAG, remotePath + " hasn't been created");
return result;
}

if (remoteFolderOperationResult.isSuccess() &&
remoteFolderOperationResult.getData().get(0) instanceof RemoteFile remoteFile) {
createdRemoteFolder = remoteFile;
}
final var readResult = new ReadFolderRemoteOperation(remotePath).execute(client);
final var readData = readResult.getData();

saveFolderInDB();
} else {
Log_OC.e(TAG, remotePath + " hasn't been created");
if (!readResult.isSuccess() || readData == null || readData.isEmpty() ||
!(readData.get(0) instanceof RemoteFile remoteFolder)) {
Log_OC.e(TAG, remotePath + " has been created but could not be read back");
return readResult;
}

createdRemoteFolder = remoteFolder;
saveFolderInDB();

return result;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1156,6 +1156,10 @@ class FileDisplayActivity :
} else {
lifecycleScope.launch(Dispatchers.IO) {
fileDataStorageManager.addCreateFileOfflineOperation(filePaths, decryptedRemotePaths)

withContext(Dispatchers.Main) {
refreshCurrentDirectory()
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,28 +196,29 @@ class CreateFolderDialogFragment :
val path = parentFolder?.decryptedRemotePath + newFolderName + OCFile.PATH_SEPARATOR

val componentGetter = typedActivity<ComponentsGetter>()
val fda = typedActivity<FileDisplayActivity>()
connectivityService.isNetworkAndServerAvailable {
if (it) {
componentGetter?.fileOperationsHelper?.createFolder(path, encrypted)
} else {
Log_OC.d(TAG, "Network not available, creating offline operation")
lifecycleScope.launch(Dispatchers.IO) {
fileDataStorageManager.addCreateFolderOfflineOperation(
path,
newFolderName,
parentFolder?.fileId
)

withContext(Dispatchers.Main) {
fda?.refreshCurrentDirectory()
}
}
createFolderOfflineOperation(path, newFolderName)
}
}
}
}

private fun createFolderOfflineOperation(path: String, folderName: String) {
val activity = typedActivity<FileDisplayActivity>() ?: return

activity.lifecycleScope.launch(Dispatchers.IO) {
fileDataStorageManager.addCreateFolderOfflineOperation(path, folderName, parentFolder?.fileId)

withContext(Dispatchers.Main) {
activity.refreshCurrentDirectory()
}
}
}

companion object {
private const val TAG = "CreateFolderDialogFragment"
private const val ARG_PARENT_FOLDER = "PARENT_FOLDER"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,12 @@ class RemoveFilesDialogFragment :
?.partition { it.isOfflineOperation }
?: (emptyList<OCFile>() to emptyList())

offlineFiles.forEach(fileDataStorageManager::deleteOfflineOperation)

val listener = getTypedActivity(OnFilesRemovedListener::class.java)
val fileActivity = getTypedActivity(FileActivity::class.java)

fileActivity?.lifecycleScope?.launch(Dispatchers.IO) {
offlineFiles.forEach(fileDataStorageManager::deleteOfflineOperation)

val (autoUploadEntities, filesToRemove) =
FileUploadHelper.instance().splitFilesByAutoUpload(files, userAccountManager.user.accountName)
withContext(Dispatchers.Main) {
Expand Down
Loading