123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 |
- /*
- * Nextcloud Android client application
- *
- * @author Mario Danic
- * @author Chris Narkiewicz
- * Copyright (C) 2017 Mario Danic
- * Copyright (C) 2017 Nextcloud
- * Copyright (C) 2919 Chris Narkiewicz
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
- *
- * You should have received a copy of the GNU Affero General Public
- * License along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- package com.owncloud.android.jobs;
- import android.accounts.Account;
- import android.content.ContentResolver;
- import android.content.Context;
- import android.content.res.Resources;
- import android.os.Build;
- import android.os.PowerManager;
- import android.text.TextUtils;
- import com.evernote.android.job.Job;
- import com.evernote.android.job.util.support.PersistableBundleCompat;
- import com.nextcloud.client.account.UserAccountManager;
- import com.nextcloud.client.preferences.AppPreferences;
- import com.owncloud.android.MainApp;
- import com.owncloud.android.R;
- import com.owncloud.android.authentication.AccountUtils;
- import com.owncloud.android.datamodel.ArbitraryDataProvider;
- import com.owncloud.android.datamodel.FilesystemDataProvider;
- import com.owncloud.android.datamodel.MediaFolderType;
- import com.owncloud.android.datamodel.SyncedFolder;
- import com.owncloud.android.datamodel.SyncedFolderProvider;
- import com.owncloud.android.datamodel.UploadsStorageManager;
- 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.utils.FileStorageUtils;
- import com.owncloud.android.utils.FilesSyncHelper;
- import com.owncloud.android.utils.MimeType;
- import com.owncloud.android.utils.MimeTypeUtil;
- import com.owncloud.android.utils.PowerUtils;
- import java.io.File;
- import java.text.ParsePosition;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.Locale;
- import java.util.TimeZone;
- import androidx.annotation.NonNull;
- import androidx.exifinterface.media.ExifInterface;
- /*
- Job that:
- - restarts existing jobs if required
- - finds new and modified files since we last run this
- - creates upload tasks
- */
- public class FilesSyncJob extends Job {
- public static final String TAG = "FilesSyncJob";
- public static final String SKIP_CUSTOM = "skipCustom";
- public static final String OVERRIDE_POWER_SAVING = "overridePowerSaving";
- private static final String WAKELOCK_TAG_SEPARATION = ":";
- private UserAccountManager userAccountManager;
- private AppPreferences preferences;
- private UploadsStorageManager uploadsStorageManager;
- public FilesSyncJob(
- final UserAccountManager userAccountManager,
- final AppPreferences preferences,
- final UploadsStorageManager uploadsStorageManager
- ) {
- this.userAccountManager = userAccountManager;
- this.preferences = preferences;
- this.uploadsStorageManager = uploadsStorageManager;
- }
- @NonNull
- @Override
- protected Result onRunJob(@NonNull Params params) {
- final Context context = MainApp.getAppContext();
- PowerManager.WakeLock wakeLock = null;
- if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
- PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, MainApp.getAuthority() +
- WAKELOCK_TAG_SEPARATION + TAG);
- wakeLock.acquire(10 * 60 * 1000);
- }
- PersistableBundleCompat bundle = params.getExtras();
- final boolean overridePowerSaving = bundle.getBoolean(OVERRIDE_POWER_SAVING, false);
- // If we are in power save mode, better to postpone upload
- if (PowerUtils.isPowerSaveMode(context) && !overridePowerSaving) {
- wakeLock.release();
- return Result.SUCCESS;
- }
- Resources resources = MainApp.getAppContext().getResources();
- boolean lightVersion = resources.getBoolean(R.bool.syncedFolder_light);
- final boolean skipCustom = bundle.getBoolean(SKIP_CUSTOM, false);
- FilesSyncHelper.restartJobsIfNeeded(uploadsStorageManager, userAccountManager);
- FilesSyncHelper.insertAllDBEntries(preferences, skipCustom);
- // Create all the providers we'll need
- final ContentResolver contentResolver = context.getContentResolver();
- final FilesystemDataProvider filesystemDataProvider = new FilesystemDataProvider(contentResolver);
- SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(contentResolver,
- preferences);
- Locale currentLocale = context.getResources().getConfiguration().locale;
- SimpleDateFormat sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss", currentLocale);
- sFormatter.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getID()));
- FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
- for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) {
- if ((syncedFolder.isEnabled()) && (!skipCustom || MediaFolderType.CUSTOM != syncedFolder.getType())) {
- syncFolder(context, resources, lightVersion, filesystemDataProvider, currentLocale, sFormatter,
- requester, syncedFolder);
- }
- }
- if (wakeLock != null) {
- wakeLock.release();
- }
- return Result.SUCCESS;
- }
- private void syncFolder(Context context, Resources resources, boolean lightVersion,
- FilesystemDataProvider filesystemDataProvider, Locale currentLocale,
- SimpleDateFormat sFormatter, FileUploader.UploadRequester requester,
- SyncedFolder syncedFolder) {
- String remotePath;
- boolean subfolderByDate;
- Integer uploadAction;
- boolean needsCharging;
- boolean needsWifi;
- File file;
- ArbitraryDataProvider arbitraryDataProvider;
- Account account = AccountUtils.getOwnCloudAccountByName(context, syncedFolder.getAccount());
- if (lightVersion) {
- arbitraryDataProvider = new ArbitraryDataProvider(context.getContentResolver());
- } else {
- arbitraryDataProvider = null;
- }
- for (String path : filesystemDataProvider.getFilesForUpload(syncedFolder.getLocalPath(),
- Long.toString(syncedFolder.getId()))) {
- file = new File(path);
- Long lastModificationTime = calculateLastModificationTime(file, syncedFolder, sFormatter);
- String mimeType = MimeTypeUtil.getBestMimeTypeByFilename(file.getAbsolutePath());
- if (lightVersion) {
- needsCharging = resources.getBoolean(R.bool.syncedFolder_light_on_charging);
- needsWifi = account == null || arbitraryDataProvider.getBooleanValue(account.name,
- SettingsActivity.SYNCED_FOLDER_LIGHT_UPLOAD_ON_WIFI);
- String uploadActionString = resources.getString(R.string.syncedFolder_light_upload_behaviour);
- uploadAction = getUploadAction(uploadActionString);
- subfolderByDate = resources.getBoolean(R.bool.syncedFolder_light_use_subfolders);
- remotePath = resources.getString(R.string.syncedFolder_remote_folder);
- } else {
- needsCharging = syncedFolder.getChargingOnly();
- needsWifi = syncedFolder.getWifiOnly();
- uploadAction = syncedFolder.getUploadAction();
- subfolderByDate = syncedFolder.getSubfolderByDate();
- remotePath = syncedFolder.getRemotePath();
- }
- if (!subfolderByDate) {
- String adaptedPath = file.getAbsolutePath()
- .replace(syncedFolder.getLocalPath(), "")
- .replace("/" + file.getName(), "");
- remotePath += adaptedPath;
- }
- requester.uploadFileWithOverwrite(
- context,
- account,
- file.getAbsolutePath(),
- FileStorageUtils.getInstantUploadFilePath(
- currentLocale,
- remotePath, file.getName(),
- lastModificationTime, subfolderByDate),
- uploadAction,
- mimeType,
- true, // create parent folder if not existent
- UploadFileOperation.CREATED_AS_INSTANT_PICTURE,
- needsWifi,
- needsCharging,
- true
- );
- filesystemDataProvider.updateFilesystemFileAsSentForUpload(path,
- Long.toString(syncedFolder.getId()));
- }
- }
- private Long calculateLastModificationTime(File file, SyncedFolder syncedFolder, SimpleDateFormat formatter) {
- Long lastModificationTime = file.lastModified();
- if (MediaFolderType.IMAGE == syncedFolder.getType()) {
- String mimeTypeString = FileStorageUtils.getMimeTypeFromName(file.getAbsolutePath());
- if (MimeType.JPEG.equalsIgnoreCase(mimeTypeString)
- || MimeType.TIFF.equalsIgnoreCase(mimeTypeString)) {
- try {
- ExifInterface exifInterface = new ExifInterface(file.getAbsolutePath());
- String exifDate = exifInterface.getAttribute(ExifInterface.TAG_DATETIME);
- if (!TextUtils.isEmpty(exifDate)) {
- ParsePosition pos = new ParsePosition(0);
- Date dateTime = formatter.parse(exifDate, pos);
- lastModificationTime = dateTime.getTime();
- }
- } catch (Exception e) {
- Log_OC.d(TAG, "Failed to get the proper time " + e.getLocalizedMessage());
- }
- }
- }
- return lastModificationTime;
- }
- private Integer getUploadAction(String action) {
- switch (action) {
- case "LOCAL_BEHAVIOUR_FORGET":
- return FileUploader.LOCAL_BEHAVIOUR_FORGET;
- case "LOCAL_BEHAVIOUR_MOVE":
- return FileUploader.LOCAL_BEHAVIOUR_MOVE;
- case "LOCAL_BEHAVIOUR_DELETE":
- return FileUploader.LOCAL_BEHAVIOUR_DELETE;
- default:
- return FileUploader.LOCAL_BEHAVIOUR_FORGET;
- }
- }
- }
|