Merge remote-tracking branch 'origin/master' into dev

This commit is contained in:
Tobias Kaminsky 2024-01-12 02:31:15 +01:00
commit 880ad62cf5
6 changed files with 215 additions and 119 deletions

View file

@ -27,6 +27,7 @@ import org.junit.runner.RunWith;
import java.io.File;
import java.util.ArrayList;
import java.util.Random;
import java.util.UUID;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@ -174,15 +175,22 @@ public class UploadStorageManagerTest extends AbstractIT {
}
}
public String generateUniqueNumber() {
UUID uuid = UUID.randomUUID();
return uuid.toString();
}
private OCUpload createUpload(Account account) {
OCUpload upload = new OCUpload(File.separator + "very long long long long long long long long long long long " +
"long long long long long long long long long long long long long long " +
"long long long long long long long long long long long long long long " +
"long long long long long long long LocalPath",
"long long long long long long long LocalPath " +
generateUniqueNumber(),
OCFile.PATH_SEPARATOR + "very long long long long long long long long long " +
"long long long long long long long long long long long long long long " +
"long long long long long long long long long long long long long long " +
"long long long long long long long long long long long long RemotePath",
"long long long long long long long long long long long long RemotePath " +
generateUniqueNumber(),
account.name);
upload.setFileSize(new Random().nextInt(20000) * 10000);

View file

@ -384,6 +384,12 @@
</intent-filter>
</activity>
<service
android:name="androidx.work.impl.foreground.SystemForegroundService"
android:directBootAware="false"
android:enabled="@bool/enable_system_foreground_service_default"
android:exported="false"
android:foregroundServiceType="dataSync" />
<service
android:name=".services.OperationsService"
android:exported="false" />

View file

@ -25,9 +25,12 @@ package com.nextcloud.client.jobs
import android.content.ContentResolver
import android.content.Context
import android.content.res.Resources
import android.os.Build
import android.text.TextUtils
import androidx.core.app.NotificationCompat
import androidx.exifinterface.media.ExifInterface
import androidx.work.Worker
import androidx.work.CoroutineWorker
import androidx.work.ForegroundInfo
import androidx.work.WorkerParameters
import com.nextcloud.client.account.UserAccountManager
import com.nextcloud.client.device.PowerManagementService
@ -37,6 +40,7 @@ import com.owncloud.android.R
import com.owncloud.android.datamodel.ArbitraryDataProvider
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl
import com.owncloud.android.datamodel.FilesystemDataProvider
import com.owncloud.android.datamodel.ForegroundServiceType
import com.owncloud.android.datamodel.MediaFolderType
import com.owncloud.android.datamodel.SyncedFolder
import com.owncloud.android.datamodel.SyncedFolderProvider
@ -45,6 +49,7 @@ import com.owncloud.android.files.services.FileUploader
import com.owncloud.android.lib.common.utils.Log_OC
import com.owncloud.android.operations.UploadFileOperation
import com.owncloud.android.ui.activity.SettingsActivity
import com.owncloud.android.ui.notifications.NotificationUtils
import com.owncloud.android.utils.FileStorageUtils
import com.owncloud.android.utils.FilesSyncHelper
import com.owncloud.android.utils.MimeType
@ -66,16 +71,38 @@ class FilesSyncWork(
private val powerManagementService: PowerManagementService,
private val syncedFolderProvider: SyncedFolderProvider,
private val backgroundJobManager: BackgroundJobManager
) : Worker(context, params) {
) : CoroutineWorker(context, params) {
companion object {
const val TAG = "FilesSyncJob"
const val SKIP_CUSTOM = "skipCustom"
const val OVERRIDE_POWER_SAVING = "overridePowerSaving"
const val FOREGROUND_SERVICE_ID = 414
}
override fun doWork(): Result {
@Suppress("MagicNumber")
private fun createForegroundInfo(progressPercent: Int): ForegroundInfo {
// update throughout worker execution to give use feedback how far worker is
val notification = NotificationCompat.Builder(context, NotificationUtils.NOTIFICATION_CHANNEL_FILE_SYNC)
.setTicker(context.getString(R.string.autoupload_worker_foreground_info))
.setContentText(context.getString(R.string.autoupload_worker_foreground_info))
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle(context.getString(R.string.autoupload_worker_foreground_info))
.setOngoing(true)
.setProgress(100, progressPercent, false)
.build()
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ForegroundInfo(FOREGROUND_SERVICE_ID, notification, ForegroundServiceType.DataSync.getId())
} else {
ForegroundInfo(FOREGROUND_SERVICE_ID, notification)
}
}
@Suppress("MagicNumber")
override suspend fun doWork(): Result {
backgroundJobManager.logStartOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class))
setForeground(createForegroundInfo(0))
val overridePowerSaving = inputData.getBoolean(OVERRIDE_POWER_SAVING, false)
// If we are in power save mode, better to postpone upload
@ -93,13 +120,18 @@ class FilesSyncWork(
connectivityService,
powerManagementService
)
setForeground(createForegroundInfo(5))
FilesSyncHelper.insertAllDBEntries(skipCustom, syncedFolderProvider)
setForeground(createForegroundInfo(50))
// Create all the providers we'll need
val filesystemDataProvider = FilesystemDataProvider(contentResolver)
val currentLocale = resources.configuration.locale
val dateFormat = SimpleDateFormat("yyyy:MM:dd HH:mm:ss", currentLocale)
dateFormat.timeZone = TimeZone.getTimeZone(TimeZone.getDefault().id)
for (syncedFolder in syncedFolderProvider.syncedFolders) {
val syncedFolders = syncedFolderProvider.syncedFolders
for ((index, syncedFolder) in syncedFolders.withIndex()) {
setForeground(createForegroundInfo((50 + (index.toDouble() / syncedFolders.size.toDouble()) * 50).toInt()))
if (syncedFolder.isEnabled && (!skipCustom || MediaFolderType.CUSTOM != syncedFolder.type)) {
syncFolder(
context,

View file

@ -225,9 +225,10 @@ public class FilesystemDataProvider {
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(filepath))) {
CRC32 crc = new CRC32();
int cnt;
while ((cnt = inputStream.read()) != -1) {
crc.update(cnt);
byte[] buf = new byte[1024 * 64];
int size;
while ((size = inputStream.read(buf)) > 0) {
crc.update(buf, 0, size);
}
return crc.getValue();

View file

@ -87,6 +87,17 @@ public class UploadsStorageManager extends Observable {
* @return upload id, -1 if the insert process fails.
*/
public long storeUpload(OCUpload ocUpload) {
OCUpload existingUpload = getPendingCurrentOrFailedUpload(ocUpload);
if (existingUpload != null) {
Log_OC.v(TAG, "Will update upload in db since " + ocUpload.getLocalPath() + " already exists as " +
"pending, current or failed upload");
long existingId = existingUpload.getUploadId();
ocUpload.setUploadId(existingId);
updateUpload(ocUpload);
return existingId;
}
Log_OC.v(TAG, "Inserting " + ocUpload.getLocalPath() + " with status=" + ocUpload.getUploadStatus());
ContentValues cv = getContentValues(ocUpload);
@ -100,14 +111,26 @@ public class UploadsStorageManager extends Observable {
long new_id = Long.parseLong(result.getPathSegments().get(1));
ocUpload.setUploadId(new_id);
notifyObserversNow();
return new_id;
}
}
public long[] storeUploads(final List<OCUpload> ocUploads) {
Log_OC.v(TAG, "Inserting " + ocUploads.size() + " uploads");
ArrayList<ContentProviderOperation> operations = new ArrayList<>(ocUploads.size());
for (OCUpload ocUpload : ocUploads) {
OCUpload existingUpload = getPendingCurrentOrFailedUpload(ocUpload);
if (existingUpload != null) {
Log_OC.v(TAG, "Will update upload in db since " + ocUpload.getLocalPath() + " already exists as" +
" pending, current or failed upload");
ocUpload.setUploadId(existingUpload.getUploadId());
updateUpload(ocUpload);
continue;
}
final ContentProviderOperation operation = ContentProviderOperation
.newInsert(ProviderTableMeta.CONTENT_URI_UPLOADS)
.withValues(getContentValues(ocUpload))
@ -260,8 +283,7 @@ public class UploadsStorageManager extends Observable {
}
/**
* Should be called when some value of this DB was changed. All observers
* are informed.
* Should be called when some value of this DB was changed. All observers are informed.
*/
public void notifyObserversNow() {
Log_OC.d(TAG, "notifyObserversNow");
@ -345,6 +367,33 @@ public class UploadsStorageManager extends Observable {
return getUploads(null, (String[]) null);
}
public OCUpload getPendingCurrentOrFailedUpload(OCUpload upload) {
try (Cursor cursor = getDB().query(
ProviderTableMeta.CONTENT_URI_UPLOADS,
null,
ProviderTableMeta.UPLOADS_REMOTE_PATH + "=? and " +
ProviderTableMeta.UPLOADS_LOCAL_PATH + "=? and " +
ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "=? and (" +
ProviderTableMeta.UPLOADS_STATUS + "=? or " +
ProviderTableMeta.UPLOADS_STATUS + "=? )",
new String[]{
upload.getRemotePath(),
upload.getLocalPath(),
upload.getAccountName(),
String.valueOf(UploadStatus.UPLOAD_IN_PROGRESS.value),
String.valueOf(UploadStatus.UPLOAD_FAILED.value)
},
ProviderTableMeta.UPLOADS_REMOTE_PATH + " ASC")) {
if (cursor != null) {
if (cursor.moveToFirst()) {
return createOCUploadFromCursor(cursor);
}
}
}
return null;
}
public OCUpload getUploadByRemotePath(String remotePath) {
OCUpload result = null;
try (Cursor cursor = getDB().query(
@ -717,8 +766,7 @@ public class UploadsStorageManager extends Observable {
}
/**
* Changes the status of any in progress upload from UploadStatus.UPLOAD_IN_PROGRESS
* to UploadStatus.UPLOAD_FAILED
* Changes the status of any in progress upload from UploadStatus.UPLOAD_IN_PROGRESS to UploadStatus.UPLOAD_FAILED
*
* @return Number of uploads which status was changed.
*/

View file

@ -628,6 +628,7 @@
<string name="autoupload_hide_folder">Hide folder</string>
<string name="autoupload_configure">Configure</string>
<string name="synced_folders_configure_folders">Configure folders</string>
<string name="autoupload_worker_foreground_info">Preparing auto upload</string>
<string name="empty" translatable="false" />
<string name="test_server_button">Test server connection</string>