- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2020 ownCloud GmbH.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.owncloud.android.files.services;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.OnAccountsUpdateListener;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.Intent;
-import android.os.Binder;
-import android.os.Build;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Parcelable;
-import android.os.Process;
-import android.util.Pair;
-
-import androidx.annotation.Nullable;
-import androidx.core.app.NotificationCompat;
-import androidx.localbroadcastmanager.content.LocalBroadcastManager;
-import com.owncloud.android.R;
-import com.owncloud.android.authentication.AccountUtils;
-import com.owncloud.android.datamodel.FileDataStorageManager;
-import com.owncloud.android.datamodel.OCUpload;
-import com.owncloud.android.datamodel.UploadsStorageManager;
-import com.owncloud.android.datamodel.UploadsStorageManager.UploadStatus;
-import com.owncloud.android.db.UploadResult;
-import com.owncloud.android.domain.capabilities.model.OCCapability;
-import com.owncloud.android.domain.files.model.OCFile;
-import com.owncloud.android.lib.common.OwnCloudAccount;
-import com.owncloud.android.lib.common.OwnCloudClient;
-import com.owncloud.android.lib.common.SingleSessionManager;
-import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
-import com.owncloud.android.lib.common.operations.RemoteOperationResult;
-import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
-import com.owncloud.android.lib.resources.files.chunks.ChunkedUploadFromFileSystemOperation;
-import com.owncloud.android.lib.resources.files.chunks.RemoveRemoteChunksFolderOperation;
-import com.owncloud.android.operations.ChunkedUploadFileOperation;
-import com.owncloud.android.operations.UploadFileOperation;
-import com.owncloud.android.ui.activity.FileActivity;
-import com.owncloud.android.ui.activity.UploadListActivity;
-import com.owncloud.android.ui.errorhandling.ErrorMessageAdapter;
-import com.owncloud.android.utils.Extras;
-import com.owncloud.android.utils.NotificationUtils;
-import com.owncloud.android.utils.SecurityUtils;
-import kotlin.Unit;
-import timber.log.Timber;
-
-import java.io.File;
-import java.lang.ref.WeakReference;
-import java.util.AbstractList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Vector;
-
-import static com.owncloud.android.operations.UploadFileOperation.CREATED_AS_CAMERA_UPLOAD_PICTURE;
-import static com.owncloud.android.operations.UploadFileOperation.CREATED_AS_CAMERA_UPLOAD_VIDEO;
-import static com.owncloud.android.utils.NotificationConstantsKt.UPLOAD_NOTIFICATION_CHANNEL_ID;
-
-/**
- * Service for uploading files. Invoke using context.startService(...).
- *
- * Files to be uploaded are stored persistently using {@link UploadsStorageManager}.
- *
- * On next invocation of {@link FileUploader} uploaded files which
- * previously failed will be uploaded again until either upload succeeded or a
- * fatal error occurred.
- *
- * Every file passed to this service is uploaded. No filtering is performed.
- */
-public class FileUploader extends Service
- implements OnDatatransferProgressListener, OnAccountsUpdateListener,
- UploadFileOperation.OnRenameListener {
-
- private static final String UPLOADS_ADDED_MESSAGE = "UPLOADS_ADDED";
- private static final String UPLOAD_START_MESSAGE = "UPLOAD_START";
- private static final String UPLOAD_FINISH_MESSAGE = "UPLOAD_FINISH";
-
- protected static final String KEY_FILE = "FILE";
- protected static final String KEY_LOCAL_FILE = "LOCAL_FILE";
- protected static final String KEY_REMOTE_FILE = "REMOTE_FILE";
- protected static final String KEY_MIME_TYPE = "MIME_TYPE";
- protected static final String KEY_IS_AVAILABLE_OFFLINE_FILE = "KEY_IS_AVAILABLE_OFFLINE_FILE";
- protected static final String KEY_REQUESTED_FROM_WIFI_BACK_EVENT = "KEY_REQUESTED_FROM_WIFI_BACK_EVENT";
-
- /**
- * Call this Service with only this Intent key if all pending uploads are to be retried.
- */
- protected static final String KEY_RETRY = "KEY_RETRY";
- /**
- * Call this Service with KEY_RETRY and KEY_RETRY_UPLOAD to retry
- * upload of file identified by KEY_RETRY_UPLOAD.
- */
- protected static final String KEY_RETRY_UPLOAD = "KEY_RETRY_UPLOAD";
- /**
- * {@link Account} to which file is to be uploaded.
- */
- protected static final String KEY_ACCOUNT = "ACCOUNT";
-
- /**
- * Set to true if remote file is to be overwritten. Default action is to upload with different name.
- */
- protected static final String KEY_FORCE_OVERWRITE = "KEY_FORCE_OVERWRITE";
- /**
- * Key to signal what is the origin of the upload request
- */
- protected static final String KEY_CREATED_BY = "CREATED_BY";
-
- protected static final String KEY_LOCAL_BEHAVIOUR = "BEHAVIOUR";
-
- public static final int LEGACY_LOCAL_BEHAVIOUR_COPY = 0;
- public static final int LEGACY_LOCAL_BEHAVIOUR_MOVE = 1;
- public static final int LEGACY_LOCAL_BEHAVIOUR_FORGET = 2;
-
- private OwnCloudClient mUploadClient = null;
- private Account mCurrentAccount = null;
- private FileDataStorageManager mStorageManager;
- //since there can be only one instance of an Android service, there also just one db connection.
- private UploadsStorageManager mUploadsStorageManager = null;
-
- private IndexedForest mPendingUploads = new IndexedForest<>();
-
- private LocalBroadcastManager mLocalBroadcastManager;
-
- /**
- * {@link UploadFileOperation} object of ongoing upload. Can be null. Note: There can only be one concurrent upload!
- */
- private UploadFileOperation mCurrentUpload = null;
-
- private NotificationManager mNotificationManager;
- private NotificationCompat.Builder mNotificationBuilder;
- private int mLastPercent;
-
- public static String getUploadsAddedMessage() {
- return FileUploader.class.getName() + UPLOADS_ADDED_MESSAGE;
- }
-
- public static String getUploadStartMessage() {
- return FileUploader.class.getName() + UPLOAD_START_MESSAGE;
- }
-
- public static String getUploadFinishMessage() {
- return FileUploader.class.getName() + UPLOAD_FINISH_MESSAGE;
- }
-
- @Override
- public void onRenameUpload() {
- mUploadsStorageManager.updateDatabaseUploadStart(mCurrentUpload);
- sendBroadcastUploadStarted(mCurrentUpload);
- }
-
- /**
- * Service initialization
- */
- @Override
- public void onCreate() {
- super.onCreate();
- Timber.d("Creating service");
-
- mNotificationBuilder = NotificationUtils.newNotificationBuilder(this, UPLOAD_NOTIFICATION_CHANNEL_ID);
-
- HandlerThread thread = new HandlerThread("FileUploaderThread",
- Process.THREAD_PRIORITY_BACKGROUND);
- thread.start();
-
- mUploadsStorageManager = new UploadsStorageManager(getContentResolver());
-
- int failedCounter = mUploadsStorageManager.failInProgressUploads(
- UploadResult.SERVICE_INTERRUPTED // Add UploadResult.KILLED?
- );
- if (failedCounter > 0) {
- resurrection();
- }
-
- // add AccountsUpdatedListener
- AccountManager am = AccountManager.get(getApplicationContext());
- am.addOnAccountsUpdatedListener(this, null, false);
-
- // create manager for local broadcasts
- mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
- }
-
- /**
- * Service clean-up when restarted after being killed
- */
- private void resurrection() {
- // remove stucked notification
- getNotificationManager().cancel(R.string.uploader_upload_in_progress_ticker);
- }
-
- /**
- * Service clean up
- */
- @Override
- public void onDestroy() {
- Timber.v("Destroying service");
- mNotificationManager = null;
-
- // remove AccountsUpdatedListener
- AccountManager am = AccountManager.get(getApplicationContext());
- am.removeOnAccountsUpdatedListener(this);
-
- super.onDestroy();
- }
-
- @Nullable
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-
- /**
- * Entry point to add one or several files to the queue of uploads.
- *
- * New uploads are added calling to startService(), resulting in a call to
- * this method. This ensures the service will keep on working although the
- * caller activity goes away.
- */
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- Timber.d("Starting command with id %s", startId);
-
- int createdBy = intent.getIntExtra(KEY_CREATED_BY, UploadFileOperation.CREATED_BY_USER);
- boolean isCameraUploadFile =
- createdBy == CREATED_AS_CAMERA_UPLOAD_PICTURE || createdBy == CREATED_AS_CAMERA_UPLOAD_VIDEO;
- boolean isAvailableOfflineFile = intent.getBooleanExtra(KEY_IS_AVAILABLE_OFFLINE_FILE, false);
- boolean isRequestedFromWifiBackEvent = intent.getBooleanExtra(
- KEY_REQUESTED_FROM_WIFI_BACK_EVENT, false
- );
-
- if ((isCameraUploadFile || isAvailableOfflineFile || isRequestedFromWifiBackEvent) &&
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- Timber.d("Starting FileUploader service in foreground");
-
- if (isCameraUploadFile) {
- mNotificationBuilder.setContentTitle(getString(R.string.uploader_upload_camera_upload_files));
- } else if (isAvailableOfflineFile) {
- mNotificationBuilder.setContentTitle(getString(R.string.uploader_upload_available_offline_files));
- } else if (isRequestedFromWifiBackEvent) {
- mNotificationBuilder.setContentTitle(getString(R.string.uploader_upload_requested_from_wifi_files));
- }
-
- /*
- * After calling startForegroundService method for camera uploads or
- * available offline, we have to call this within five seconds after the service is created to avoid
- * an error
- */
- startForeground(141, mNotificationBuilder.build());
- }
-
- boolean retry = intent.getBooleanExtra(KEY_RETRY, false);
- AbstractList requestedUploads = new Vector<>();
-
- if (!intent.hasExtra(KEY_ACCOUNT)) {
- Timber.e("Not enough information provided in intent");
- return Service.START_NOT_STICKY;
- }
-
- Account account = intent.getParcelableExtra(KEY_ACCOUNT);
- Timber.d("Account to upload the file to: %s", account);
- if (account == null || !AccountUtils.exists(account.name, getApplicationContext())) {
- return Service.START_NOT_STICKY;
- }
-
- if (!retry) {
- if (!(intent.hasExtra(KEY_LOCAL_FILE) || intent.hasExtra(KEY_FILE))) {
- Timber.e("Not enough information provided in intent");
- return Service.START_NOT_STICKY;
- }
-
- String[] localPaths = null, remotePaths = null, mimeTypes = null;
- OCFile[] files = null;
-
- if (intent.hasExtra(KEY_FILE)) {
- Parcelable[] files_temp = intent.getParcelableArrayExtra(KEY_FILE);
- files = new OCFile[files_temp.length];
- System.arraycopy(files_temp, 0, files, 0, files_temp.length);
-
- } else {
- localPaths = intent.getStringArrayExtra(KEY_LOCAL_FILE);
- remotePaths = intent.getStringArrayExtra(KEY_REMOTE_FILE);
- mimeTypes = intent.getStringArrayExtra(KEY_MIME_TYPE);
- }
-
- boolean forceOverwrite = intent.getBooleanExtra(KEY_FORCE_OVERWRITE, false);
- int localAction = intent.getIntExtra(KEY_LOCAL_BEHAVIOUR, LEGACY_LOCAL_BEHAVIOUR_FORGET);
-
- if (intent.hasExtra(KEY_FILE) && files == null) {
- Timber.e("Incorrect array for OCFiles provided in upload intent");
- return Service.START_NOT_STICKY;
-
- } else if (!intent.hasExtra(KEY_FILE)) {
- if (localPaths == null) {
- Timber.e("Incorrect array for local paths provided in upload intent");
- return Service.START_NOT_STICKY;
- }
- if (remotePaths == null) {
- Timber.e("Incorrect array for remote paths provided in upload intent");
- return Service.START_NOT_STICKY;
- }
- if (localPaths.length != remotePaths.length) {
- Timber.e("Different number of remote paths and local paths!");
- return Service.START_NOT_STICKY;
- }
-
- files = new OCFile[localPaths.length];
- for (int i = 0; i < localPaths.length; i++) {
- files[i] = UploadFileOperation.obtainNewOCFileToUpload(
- remotePaths[i],
- localPaths[i],
- ((mimeTypes != null) ? mimeTypes[i] : null),
- getApplicationContext()
- );
- if (files[i] == null) {
- Timber.e("obtainNewOCFileToUpload() returned null for remotePaths[i]:" + remotePaths[i]
- + " and localPaths[i]:" + localPaths[i]);
- return Service.START_NOT_STICKY;
- }
- }
- }
- // at this point variable "OCFile[] files" is loaded correctly.
-
- String uploadKey;
- UploadFileOperation newUploadFileOperation;
- try {
- FileDataStorageManager storageManager = new FileDataStorageManager(
- getApplicationContext(),
- account,
- getContentResolver()
- );
- OCCapability capabilitiesForAccount = storageManager.getCapability(account.name);
- boolean isChunkingAllowed =
- capabilitiesForAccount != null && capabilitiesForAccount.isChunkingAllowed();
- Timber.d("Chunking is allowed: %s", isChunkingAllowed);
- for (OCFile ocFile : files) {
-
- OCUpload ocUpload = new OCUpload(ocFile, account);
- ocUpload.setFileSize(ocFile.getLength());
- ocUpload.setForceOverwrite(forceOverwrite);
- ocUpload.setCreatedBy(createdBy);
- ocUpload.setLocalAction(localAction);
- /*ocUpload.setUseWifiOnly(isUseWifiOnly);
- ocUpload.setWhileChargingOnly(isWhileChargingOnly);*/
- ocUpload.setUploadStatus(UploadStatus.UPLOAD_IN_PROGRESS);
-
- if (new File(ocFile.getStoragePath()).length() >
- ChunkedUploadFromFileSystemOperation.CHUNK_SIZE && isChunkingAllowed) {
- ocUpload.setTransferId(
- SecurityUtils.stringToMD5Hash(ocFile.getRemotePath()) + System.currentTimeMillis());
- newUploadFileOperation = new ChunkedUploadFileOperation(
- account,
- ocFile,
- ocUpload,
- forceOverwrite,
- localAction,
- this
- );
- } else {
- newUploadFileOperation = new UploadFileOperation(
- account,
- ocFile,
- ocUpload,
- forceOverwrite,
- localAction,
- this
- );
- }
-
- newUploadFileOperation.setCreatedBy(createdBy);
-
- newUploadFileOperation.addRenameUploadListener(this);
-
- Pair putResult = mPendingUploads.putIfAbsent(
- account.name,
- ocFile.getRemotePath(),
- newUploadFileOperation
- );
- if (putResult != null) {
- uploadKey = putResult.first;
- requestedUploads.add(uploadKey);
-
- // Save upload in database
- long id = mUploadsStorageManager.storeUpload(ocUpload);
- newUploadFileOperation.setOCUploadId(id);
- }
- }
-
- } catch (IllegalArgumentException e) {
- Timber.e(e, "Not enough information provided in intent: %s", e.getMessage());
- return START_NOT_STICKY;
-
- } catch (IllegalStateException e) {
- Timber.e(e, "Bad information provided in intent: %s", e.getMessage());
- return START_NOT_STICKY;
-
- } catch (Exception e) {
- Timber.e(e, "Unexpected exception while processing upload intent");
- return START_NOT_STICKY;
-
- }
- // *** TODO REWRITE: block inserted to request A retry; too many code copied, no control exception ***/
- } else {
- if (!intent.hasExtra(KEY_ACCOUNT) || !intent.hasExtra(KEY_RETRY_UPLOAD)) {
- Timber.e("Not enough information provided in intent: no KEY_RETRY_UPLOAD_KEY");
- return START_NOT_STICKY;
- }
- OCUpload upload = intent.getParcelableExtra(KEY_RETRY_UPLOAD);
-
- UploadFileOperation newUploadFileOperation;
-
- if (upload.getFileSize() > ChunkedUploadFromFileSystemOperation.CHUNK_SIZE) {
- upload.setTransferId(
- SecurityUtils.stringToMD5Hash(upload.getRemotePath()) + System.currentTimeMillis());
- newUploadFileOperation = new ChunkedUploadFileOperation(
- account,
- null,
- upload,
- upload.isForceOverwrite(),
- upload.getLocalAction(),
- this
- );
- } else {
- newUploadFileOperation = new UploadFileOperation(
- account,
- null,
- upload,
- upload.isForceOverwrite(),
- upload.getLocalAction(),
- this
- );
- }
-
- newUploadFileOperation.addRenameUploadListener(this);
-
- Pair putResult = mPendingUploads.putIfAbsent(
- account.name,
- upload.getRemotePath(),
- newUploadFileOperation
- );
- if (putResult != null) {
- String uploadKey = putResult.first;
- requestedUploads.add(uploadKey);
-
- // Update upload in database
- upload.setUploadStatus(UploadStatus.UPLOAD_IN_PROGRESS);
- mUploadsStorageManager.updateUpload(upload);
- }
- }
- // *** TODO REWRITE END ***/
-
- if (requestedUploads.size() > 0) {
- sendBroadcastUploadsAdded();
- }
- return Service.START_NOT_STICKY;
- }
-
- @Override
- public void onAccountsUpdated(Account[] accounts) {
- // Review current upload, and cancel it if its account doesn't exist
- if (mCurrentUpload != null &&
- !AccountUtils.exists(mCurrentUpload.getAccount().name, getApplicationContext())) {
- mCurrentUpload.cancel();
- }
- // The rest of uploads are cancelled when they try to start
- }
-
- /**
- * Core upload method: sends the file(s) to upload
- *
- * @param uploadKey Key to access the upload to perform, contained in mPendingUploads
- */
- public void uploadFile(String uploadKey) {
-
- mCurrentUpload = mPendingUploads.get(uploadKey);
-
- if (mCurrentUpload != null) {
-
- /// Check account existence
- if (!AccountUtils.exists(mCurrentUpload.getAccount().name, this)) {
- Timber.w("Account " + mCurrentUpload.getAccount().name + " does not exist anymore -> cancelling all " +
- "its uploads");
- cancelUploadsForAccount(mCurrentUpload.getAccount());
- return;
- }
-
- /// OK, let's upload
- mUploadsStorageManager.updateDatabaseUploadStart(mCurrentUpload);
-
- notifyUploadStart(mCurrentUpload);
-
- sendBroadcastUploadStarted(mCurrentUpload);
-
- RemoteOperationResult uploadResult = null;
-
- try {
- /// prepare client object to send the request to the ownCloud server
- if (mCurrentAccount == null ||
- !mCurrentAccount.equals(mCurrentUpload.getAccount())) {
- mCurrentAccount = mCurrentUpload.getAccount();
- mStorageManager = new FileDataStorageManager(
- getApplicationContext(),
- mCurrentAccount,
- getContentResolver()
- );
- } // else, reuse storage manager from previous operation
-
- // always get client from client manager to get fresh credentials in case of update
- OwnCloudAccount ocAccount = new OwnCloudAccount(
- mCurrentAccount,
- this
- );
- mUploadClient = SingleSessionManager.getDefaultSingleton().
- getClientFor(ocAccount, this);
-
- /// perform the upload
- uploadResult = mCurrentUpload.execute(mUploadClient, mStorageManager);
-
- } catch (Exception e) {
- Timber.e(e, "Error uploading");
- uploadResult = new RemoteOperationResult(e);
-
- } finally {
- Pair removeResult;
- if (mCurrentUpload.wasRenamed()) {
- removeResult = mPendingUploads.removePayload(
- mCurrentAccount.name,
- mCurrentUpload.getOldFile().getRemotePath()
- );
- /* TODO: grant that name is also updated for mCurrentUpload.getOCUploadId */
-
- } else {
- removeResult = mPendingUploads.removePayload(
- mCurrentAccount.name,
- mCurrentUpload.getRemotePath()
- );
- }
-
- if (uploadResult != null && !uploadResult.isSuccess()) {
-
- } else {
- String stringToLog = String.format(
- "Success OR fail without exception for %1s in %2s",
- mCurrentUpload.getRemotePath(),
- mCurrentAccount.name
- );
- Timber.v("%s", stringToLog);
- }
-
- if (uploadResult != null) {
- mUploadsStorageManager.updateDatabaseUploadResult(uploadResult, mCurrentUpload);
- /// notify result
- notifyUploadResult(mCurrentUpload, uploadResult);
- }
-
- sendBroadcastUploadFinished(mCurrentUpload, uploadResult, removeResult.second);
- }
- }
- }
-
- private void removeChunksFolder(long ocUploadId) {
- RemoveRemoteChunksFolderOperation remoteChunksFolderOperation = new RemoveRemoteChunksFolderOperation(
- String.valueOf(ocUploadId)
- );
-
- RemoteOperationResult result = remoteChunksFolderOperation.execute(mUploadClient);
-
- if (!result.isSuccess()) {
- Timber.e("Error deleting chunks folder after cancelling chunked upload");
- }
- }
-
- /**
- * Creates a status notification to show the upload progress
- *
- * @param upload Upload operation starting.
- */
- private void notifyUploadStart(UploadFileOperation upload) {
- Timber.d("Notifying upload start");
-
- // / create status notification with a progress bar
- mLastPercent = 0;
- mNotificationBuilder
- .setOngoing(true)
- .setTicker(getString(R.string.uploader_upload_in_progress_ticker))
- .setContentTitle(getString(R.string.uploader_upload_in_progress_ticker))
- .setProgress(100, 0, false)
- .setContentText(
- String.format(getString(R.string.uploader_upload_in_progress_content), 0, upload.getFileName()))
- .setWhen(System.currentTimeMillis());
-
- /// includes a pending intent in the notification showing the details
- Intent showUploadListIntent = new Intent(this, UploadListActivity.class);
- showUploadListIntent.putExtra(FileActivity.EXTRA_FILE, upload.getFile());
- showUploadListIntent.putExtra(FileActivity.EXTRA_ACCOUNT, upload.getAccount());
- showUploadListIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- mNotificationBuilder.setContentIntent(PendingIntent.getActivity(this, (int) System.currentTimeMillis(),
- showUploadListIntent, NotificationUtils.INSTANCE.getPendingIntentFlags()));
-
- if (!upload.isCameraUploadsPicture() && !upload.isCameraUploadsVideo()) {
- getNotificationManager().notify(R.string.uploader_upload_in_progress_ticker,
- mNotificationBuilder.build());
- }// else wait until the upload really start (onTransferProgress is called), so that if it's discarded
- // due to lack of Wifi, no notification is shown
- }
-
- /**
- * Callback method to update the progress bar in the status notification
- */
- @Override
- public void onTransferProgress(long progressRate, long totalTransferredSoFar,
- long totalToTransfer, String filePath) {
- int percent = (int) (100.0 * ((double) totalTransferredSoFar) / ((double) totalToTransfer));
- if (percent != mLastPercent) {
- mNotificationBuilder.setProgress(100, percent, false);
- String fileName = filePath.substring(filePath.lastIndexOf(File.separator) + 1);
- String text = String.format(getString(R.string.uploader_upload_in_progress_content), percent, fileName);
- mNotificationBuilder.setContentText(text);
- getNotificationManager().notify(R.string.uploader_upload_in_progress_ticker, mNotificationBuilder.build());
- }
- mLastPercent = percent;
- }
-
- /**
- * Updates the status notification with the result of an upload operation.
- *
- * @param uploadResult Result of the upload operation.
- * @param upload Finished upload operation
- */
- private void notifyUploadResult(UploadFileOperation upload,
- RemoteOperationResult uploadResult) {
- Timber.d("NotifyUploadResult with resultCode: %s", uploadResult.getCode());
- // / cancelled operation or success -> silent removal of progress notification
- getNotificationManager().cancel(R.string.uploader_upload_in_progress_ticker);
-
- if (uploadResult.isCancelled() && upload instanceof ChunkedUploadFileOperation) {
- removeChunksFolder(upload.getOCUploadId());
- }
-
- if (!uploadResult.isCancelled() &&
- !uploadResult.getCode().equals(ResultCode.DELAYED_FOR_WIFI)) {
-
- // Show the result: success or fail notification
- int tickerId = (uploadResult.isSuccess()) ? R.string.uploader_upload_succeeded_ticker :
- R.string.uploader_upload_failed_ticker;
-
- String content;
-
- // check credentials error
- boolean needsToUpdateCredentials = (ResultCode.UNAUTHORIZED.equals(uploadResult.getCode()));
- tickerId = (needsToUpdateCredentials) ?
- R.string.uploader_upload_failed_credentials_error : tickerId;
-
- mNotificationBuilder
- .setTicker(getString(tickerId))
- .setContentTitle(getString(tickerId))
- .setAutoCancel(true)
- .setOngoing(false)
- .setProgress(0, 0, false);
-
- content = ErrorMessageAdapter.Companion.getResultMessage(
- uploadResult, upload, getResources()
- );
-
- if (needsToUpdateCredentials) {
- // let the user update credentials with one click
- PendingIntent pendingIntentToRefreshCredentials =
- NotificationUtils.INSTANCE.composePendingIntentToRefreshCredentials(this, upload.getAccount());
-
- mNotificationBuilder.setContentIntent(pendingIntentToRefreshCredentials);
-
- } else {
- mNotificationBuilder.setContentText(content);
- }
-
- if (!uploadResult.isSuccess() && !needsToUpdateCredentials) {
- //in case of failure, do not show details file view (because there is no file!)
- Intent showUploadListIntent = new Intent(this, UploadListActivity.class);
- showUploadListIntent.putExtra(FileActivity.EXTRA_FILE, upload.getFile());
- showUploadListIntent.putExtra(FileActivity.EXTRA_ACCOUNT, upload.getAccount());
- showUploadListIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- mNotificationBuilder.setContentIntent(PendingIntent.getActivity(this, (int) System.currentTimeMillis(),
- showUploadListIntent, NotificationUtils.INSTANCE.getPendingIntentFlags()));
- }
-
- mNotificationBuilder.setContentText(content);
-
- getNotificationManager().notify(tickerId, mNotificationBuilder.build());
-
- if (uploadResult.isSuccess()) {
- mPendingUploads.remove(upload.getAccount().name, upload.getFile().getRemotePath());
- // remove success notification, with a delay of 2 seconds
- NotificationUtils.cancelWithDelay(
- mNotificationManager,
- R.string.uploader_upload_succeeded_ticker,
- 2000);
-
- }
- }
- }
-
- /**
- * Sends a broadcast in order to the interested activities can update their
- * view
- *
- * TODO - no more broadcasts, replace with a callback to subscribed listeners
- */
- private void sendBroadcastUploadsAdded() {
- Intent start = new Intent(getUploadsAddedMessage());
- // nothing else needed right now
- mLocalBroadcastManager.sendBroadcast(start);
- }
-
- /**
- * Sends a broadcast in order to the interested activities can update their
- * view
- *
- * TODO - no more broadcasts, replace with a callback to subscribed listeners
- *
- * @param upload Finished upload operation
- */
- private void sendBroadcastUploadStarted(
- UploadFileOperation upload) {
-
- Intent start = new Intent(getUploadStartMessage());
- start.putExtra(Extras.EXTRA_REMOTE_PATH, upload.getRemotePath()); // real remote
- start.putExtra(Extras.EXTRA_OLD_FILE_PATH, upload.getOriginalStoragePath());
- start.putExtra(Extras.EXTRA_ACCOUNT_NAME, upload.getAccount().name);
-
- mLocalBroadcastManager.sendBroadcast(start);
- }
-
- /**
- * Sends a broadcast in order to the interested activities can update their
- * view
- *
- * TODO - no more broadcasts, replace with a callback to subscribed listeners
- *
- * @param upload Finished upload operation
- * @param uploadResult Result of the upload operation
- * @param unlinkedFromRemotePath Path in the uploads tree where the upload was unlinked from
- */
- private void sendBroadcastUploadFinished(
- UploadFileOperation upload,
- RemoteOperationResult uploadResult,
- String unlinkedFromRemotePath) {
-
- Intent end = new Intent(getUploadFinishMessage());
- end.putExtra(Extras.EXTRA_REMOTE_PATH, upload.getRemotePath()); // real remote
- // path, after
- // possible
- // automatic
- // renaming
- if (upload.wasRenamed()) {
- end.putExtra(Extras.EXTRA_OLD_REMOTE_PATH, upload.getOldFile().getRemotePath());
- }
- end.putExtra(Extras.EXTRA_OLD_FILE_PATH, upload.getOriginalStoragePath());
- end.putExtra(Extras.EXTRA_ACCOUNT_NAME, upload.getAccount().name);
- end.putExtra(Extras.EXTRA_UPLOAD_RESULT, uploadResult.isSuccess());
- if (unlinkedFromRemotePath != null) {
- end.putExtra(Extras.EXTRA_LINKED_TO_PATH, unlinkedFromRemotePath);
- }
-
- mLocalBroadcastManager.sendBroadcast(end);
- }
-
- /**
- * Remove and 'forgets' pending uploads of an account.
- *
- * @param account Account which uploads will be cancelled
- */
- private void cancelUploadsForAccount(Account account) {
- mPendingUploads.remove(account.name);
- mUploadsStorageManager.removeUploads(account.name);
- }
-
- private NotificationManager getNotificationManager() {
- if (mNotificationManager == null) {
- mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
- }
- return mNotificationManager;
- }
-}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/operations/ChunkedUploadFileOperation.java b/owncloudApp/src/main/java/com/owncloud/android/operations/ChunkedUploadFileOperation.java
deleted file mode 100644
index 338f0b49eb5..00000000000
--- a/owncloudApp/src/main/java/com/owncloud/android/operations/ChunkedUploadFileOperation.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/**
- * ownCloud Android client application
- *
- * @author David González Verdugo
- * Copyright (C) 2020 ownCloud GmbH.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.owncloud.android.operations;
-
-import android.accounts.Account;
-import android.content.Context;
-
-import com.owncloud.android.datamodel.OCUpload;
-import com.owncloud.android.domain.files.model.OCFile;
-import com.owncloud.android.lib.common.OwnCloudClient;
-import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
-import com.owncloud.android.lib.common.operations.OperationCancelledException;
-import com.owncloud.android.lib.common.operations.RemoteOperationResult;
-import com.owncloud.android.lib.resources.files.FileUtils;
-import com.owncloud.android.lib.resources.files.chunks.ChunkedUploadFromFileSystemOperation;
-import com.owncloud.android.lib.resources.files.services.implementation.OCChunkService;
-import com.owncloud.android.operations.common.SyncOperation;
-
-import java.io.File;
-import java.util.Iterator;
-
-public class ChunkedUploadFileOperation extends UploadFileOperation {
-
- private String mTransferId;
-
- public ChunkedUploadFileOperation(Account account, OCFile file, OCUpload upload, boolean forceOverwrite,
- int localBehaviour, Context context) {
- super(account, file, upload, forceOverwrite, localBehaviour, context);
- mTransferId = upload.getTransferId();
- }
-
- @Override
- protected RemoteOperationResult uploadRemoteFile(OwnCloudClient client, File temporalFile, File originalFile,
- String expectedPath, File expectedFile, String timeStamp) {
- try {
- RemoteOperationResult result;
-
- // Step 1, create folder where we put the uploaded file chunks
- result = createChunksFolder(String.valueOf(mTransferId));
-
- if (!result.isSuccess()) {
- return result;
- }
-
- // Step 2, start to upload chunks
- mUploadOperation = new ChunkedUploadFromFileSystemOperation(mTransferId, mFile.getStoragePath(),
- mFile.getRemotePath(), mFile.getMimeType(), timeStamp, mFile.getEtagInConflict());
-
- // TODO: Chunks should be moved to a worker and in that case, we need to add the transfer
- // progress listener in some way.
-// Iterator listener = mDataTransferListeners.iterator();
-// while (listener.hasNext()) {
-// mUploadOperation.addDataTransferProgressListener(listener.next());
-// }
-
- if (mCancellationRequested.get()) {
- throw new OperationCancelledException();
- }
-
- result = mUploadOperation.execute(client);
-
- // File chunks not properly uploaded
- if (!result.isSuccess()) {
- return result;
- }
-
- // Step 3, move remote file to final remote destination
- moveChunksFileToFinalDestination(timeStamp, originalFile.length());
-
- // Step 4, move local file to final local destination
- moveTemporalOriginalFiles(temporalFile, originalFile, expectedPath, expectedFile);
-
- return result;
- } catch (Exception e) {
- return new RemoteOperationResult(e);
- }
- }
-
- private RemoteOperationResult createChunksFolder(String remoteChunksFolder) {
- SyncOperation syncOperation = new CreateChunksFolderOperation(remoteChunksFolder);
- return syncOperation.execute(getClient(), getStorageManager());
- }
-
- private void moveChunksFileToFinalDestination(String fileLastModifTimestamp, long fileLength) {
- OCChunkService ocChunkService = new OCChunkService(getClient());
- ocChunkService.moveFile(
- mTransferId + File.separator + FileUtils.FINAL_CHUNKS_FILE,
- mFile.getRemotePath(),
- fileLastModifTimestamp,
- fileLength
- );
- }
-}
\ No newline at end of file
diff --git a/owncloudApp/src/main/java/com/owncloud/android/operations/CreateChunksFolderOperation.java b/owncloudApp/src/main/java/com/owncloud/android/operations/CreateChunksFolderOperation.java
deleted file mode 100644
index 6442f873395..00000000000
--- a/owncloudApp/src/main/java/com/owncloud/android/operations/CreateChunksFolderOperation.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
- * ownCloud Android client application
- *
- * @author David González Verdugo
- * Copyright (C) 2020 ownCloud GmbH.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.owncloud.android.operations;
-
-import com.owncloud.android.lib.common.OwnCloudClient;
-import com.owncloud.android.lib.common.operations.RemoteOperationResult;
-import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation;
-import timber.log.Timber;
-
-public class CreateChunksFolderOperation extends CreateFolderOperation {
-
- /**
- * Constructor
- *
- * @param remotePath Path in which create the chunks folder in server
- */
- public CreateChunksFolderOperation(String remotePath) {
- super(remotePath, false);
- }
-
- @Override
- protected RemoteOperationResult run(OwnCloudClient client) {
- CreateRemoteFolderOperation createRemoteFolderOperation = new CreateRemoteFolderOperation(
- mRemotePath,
- mCreateFullPath,
- true
- );
-
- RemoteOperationResult result = createRemoteFolderOperation.execute(client);
-
- if (result.isSuccess()) {
- Timber.w("Remote chunks folder " + mRemotePath + " was created");
- } else {
- Timber.e("%s hasn't been created", mRemotePath);
- }
-
- return result;
- }
-}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/operations/CreateFolderOperation.java b/owncloudApp/src/main/java/com/owncloud/android/operations/CreateFolderOperation.java
deleted file mode 100644
index 614267cf3da..00000000000
--- a/owncloudApp/src/main/java/com/owncloud/android/operations/CreateFolderOperation.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/**
- * ownCloud Android client application
- *
- * @author David A. Velasco
- * @author masensio
- * @author David González Verdugo
- * Copyright (C) 2020 ownCloud GmbH.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.owncloud.android.operations;
-
-import com.owncloud.android.domain.files.model.OCFile;
-import com.owncloud.android.lib.common.OwnCloudClient;
-import com.owncloud.android.lib.common.operations.RemoteOperationResult;
-import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation;
-import com.owncloud.android.operations.common.SyncOperation;
-import com.owncloud.android.utils.FileStorageUtils;
-import timber.log.Timber;
-
-import java.io.File;
-
-/**
- * Access to remote operation performing the creation of a new folder in the ownCloud server.
- * Save the new folder in Database
- */
-@Deprecated
-// Call usecase instead of this operation. Remove as soon as possible-
-public class CreateFolderOperation extends SyncOperation {
-
- protected String mRemotePath;
- protected boolean mCreateFullPath;
-
- /**
- * Constructor
- *
- * @param createFullPath 'True' means that all the ancestor folders should be created
- * if don't exist yet.
- */
- public CreateFolderOperation(String remotePath, boolean createFullPath) {
- mRemotePath = remotePath;
- mCreateFullPath = createFullPath;
- }
-
- @Override
- protected RemoteOperationResult run(OwnCloudClient client) {
- CreateRemoteFolderOperation createRemoteFolderOperation = new CreateRemoteFolderOperation(
- mRemotePath,
- mCreateFullPath,
- false
- );
- RemoteOperationResult result = createRemoteFolderOperation.execute(client);
-
- if (result.isSuccess()) {
- OCFile newDir = saveFolderInDB();
- String localPath = FileStorageUtils.getDefaultSavePathFor(
- getStorageManager().getAccount().name, newDir
- );
- File localFile = new File(localPath);
- boolean created = localFile.mkdirs();
- if (!created) {
- Timber.w("Local folder " + localPath + " was not fully created");
- }
- } else {
- Timber.e("%s hasn't been created", mRemotePath);
- }
-
- return result;
- }
-
- /**
- * Save new directory in local database
- *
- * @return Instance of {@link OCFile} just created
- */
- private OCFile saveFolderInDB() {
- OCFile newDir = null;
- if (mCreateFullPath && getStorageManager().
- getFileByPath(FileStorageUtils.getParentPath(mRemotePath)) == null) {// When parent
- // of remote path
- // is not created
- String[] subFolders = mRemotePath.split("/");
- String composedRemotePath = "/";
-
- // Create all the ancestors
- for (String subFolder : subFolders) {
- if (!subFolder.isEmpty()) {
- composedRemotePath = composedRemotePath + subFolder + "/";
- mRemotePath = composedRemotePath;
- newDir = saveFolderInDB();
- }
- }
- } else { // Create directory on DB
- // FIXME: 13/10/2020 : New_arch: Migration
-// newDir = new OCFile(mRemotePath);
-// newDir.setMimetype(MimeTypeConstantsKt.MIME_DIR);
-// long parentId = getStorageManager().
-// getFileByPath(FileStorageUtils.getParentPath(mRemotePath)).getFileId();
-// newDir.setParentId(parentId);
-// newDir.setModificationTimestamp(System.currentTimeMillis());
-// getStorageManager().saveFile(newDir);
-
- Timber.d("Create directory " + mRemotePath + " in Database");
- }
- return newDir;
- }
-}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java b/owncloudApp/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java
index 4c03255f3e2..ad4f1194088 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java
+++ b/owncloudApp/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java
@@ -191,7 +191,6 @@ protected RemoteOperationResult run(OwnCloudClient client) {
if (mPushOnly) {
// prevent accidental override of unnoticed change in server;
// dirty trick, more refactoring is needed, but not today;
- // works due to {@link UploadFileOperation#L364,L367}
mLocalFile.setEtagInConflict(mLocalFile.getEtag());
}
requestForUpload(mLocalFile);
@@ -202,7 +201,7 @@ protected RemoteOperationResult run(OwnCloudClient client) {
requestForDownload(mLocalFile);
// mLocalFile, not mServerFile; we want to keep the value of
// available-offline property
- // the update of local data will be done later by the FileUploader
+ // the update of local data will be done later by the usecase
// service when the upload finishes
result = new RemoteOperationResult<>(ResultCode.OK);
@@ -224,7 +223,7 @@ protected RemoteOperationResult run(OwnCloudClient client) {
}
/**
- * Requests for an upload to the FileUploader service
+ * Requests for an upload
*
* @param file OCFile object representing the file to upload
*/
@@ -236,6 +235,7 @@ private void requestForUpload(OCFile file) {
file.getStoragePath(),
file.getParentRemotePath()
);
+ uploadFileInConflictUseCase.execute(params);
mTransferWasRequested = true;
}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/owncloudApp/src/main/java/com/owncloud/android/operations/UploadFileOperation.java
deleted file mode 100644
index 7f184b79045..00000000000
--- a/owncloudApp/src/main/java/com/owncloud/android/operations/UploadFileOperation.java
+++ /dev/null
@@ -1,720 +0,0 @@
-/**
- * ownCloud Android client application
- *
- * @author David A. Velasco
- * @author David González Verdugo
- * Copyright (C) 2020 ownCloud GmbH.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.owncloud.android.operations;
-
-import android.accounts.Account;
-import android.content.Context;
-import android.net.Uri;
-
-import com.owncloud.android.datamodel.OCUpload;
-import com.owncloud.android.domain.files.model.OCFile;
-import com.owncloud.android.files.services.FileUploader;
-import com.owncloud.android.lib.common.OwnCloudClient;
-import com.owncloud.android.lib.common.http.HttpConstants;
-import com.owncloud.android.lib.common.operations.OperationCancelledException;
-import com.owncloud.android.lib.common.operations.RemoteOperation;
-import com.owncloud.android.lib.common.operations.RemoteOperationResult;
-import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
-import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation;
-import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation;
-import com.owncloud.android.lib.resources.files.RemoteFile;
-import com.owncloud.android.lib.resources.files.UploadFileFromFileSystemOperation;
-import com.owncloud.android.operations.common.SyncOperation;
-import com.owncloud.android.utils.FileStorageUtils;
-import com.owncloud.android.utils.MimetypeIconUtil;
-import com.owncloud.android.utils.RemoteFileUtils;
-import timber.log.Timber;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.channels.FileChannel;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import static com.owncloud.android.domain.files.model.MimeTypeConstantsKt.MIME_DIR;
-import static com.owncloud.android.utils.UriUtils.URI_CONTENT_SCHEME;
-
-/**
- * Operation performing the update in the ownCloud server
- * of a file that was modified locally.
- */
-public class UploadFileOperation extends SyncOperation {
-
- public static final int CREATED_BY_USER = 0;
- public static final int CREATED_AS_CAMERA_UPLOAD_PICTURE = 1;
- public static final int CREATED_AS_CAMERA_UPLOAD_VIDEO = 2;
- protected final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
- private final AtomicBoolean mUploadStarted = new AtomicBoolean(false);
- private Account mAccount;
- /**
- * OCFile which is to be uploaded.
- */
- protected OCFile mFile;
- /**
- * Original OCFile which is to be uploaded in case file had to be renamed
- * (if forceOverwrite==false and remote file already exists).
- */
- private OCFile mOldFile;
- private String mRemotePath;
-
- // LEGACY - TO BE REMOVED
- @Deprecated
- private boolean mRemoteFolderToBeCreated;
- private boolean mForceOverwrite;
- private int mLocalBehaviour;
- private int mCreatedBy;
- private boolean mWasRenamed = false;
- private long mOCUploadId;
- /**
- * Local path to file which is to be uploaded (before any possible renaming or moving).
- */
- private String mOriginalStoragePath;
- private OnRenameListener mRenameUploadListener;
- private Context mContext;
- protected UploadFileFromFileSystemOperation mUploadOperation;
-
- public UploadFileOperation(Account account,
- OCFile file,
- OCUpload upload,
- boolean forceOverwrite,
- int localBehaviour,
- Context context
- ) {
- if (account == null) {
- throw new IllegalArgumentException("Illegal NULL account in UploadFileOperation " +
- "creation");
- }
- if (upload == null) {
- throw new IllegalArgumentException("Illegal NULL file in UploadFileOperation creation");
- }
- if (upload.getLocalPath() == null || upload.getLocalPath().length() <= 0) {
- throw new IllegalArgumentException(
- "Illegal file in UploadFileOperation; storage path invalid: "
- + upload.getLocalPath());
- }
-
- mAccount = account;
- if (file == null) {
- mFile = obtainNewOCFileToUpload(
- upload.getRemotePath(),
- upload.getLocalPath(),
- upload.getMimeType(),
- context
- );
- } else {
- mFile = file;
- }
- mRemotePath = upload.getRemotePath();
- mForceOverwrite = forceOverwrite;
- mLocalBehaviour = localBehaviour;
- mOriginalStoragePath = mFile.getStoragePath();
- mContext = context;
- mOCUploadId = upload.getUploadId();
- mCreatedBy = upload.getCreatedBy();
- mRemoteFolderToBeCreated = upload.createsRemoteFolder();
-
- }
-
- public static OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType,
- Context context) {
- // FIXME: 13/10/2020 : New_arch: Upload
- // MIME type
- if (mimeType == null || mimeType.length() <= 0) {
- mimeType = MimetypeIconUtil.getBestMimeTypeByFilename(localPath);
- }
-
- OCFile newFile = new OCFile(remotePath, mimeType, (long) 0, "");
-
- newFile.setStoragePath(localPath);
- newFile.setLastSyncDateForProperties(0L);
- newFile.setLastSyncDateForData(0L);
-
- // size
- if (localPath != null && localPath.length() > 0) {
- File localFile = new File(localPath);
- newFile.setLength(localFile.length());
- newFile.setLastSyncDateForData(localFile.lastModified());
- } // don't worry about not assigning size, the problems with localPath
- // are checked when the UploadFileOperation instance is created
-
- newFile.setMimeType(mimeType);
-
- return newFile;
- }
-
- public Account getAccount() {
- return mAccount;
- }
-
- public String getFileName() {
- return (mFile != null) ? mFile.getFileName() : null;
- }
-
- public OCFile getFile() {
- return mFile;
- }
-
- /**
- * If remote file was renamed, return original OCFile which was uploaded. Is
- * null is file was not renamed.
- */
- public OCFile getOldFile() {
- return mOldFile;
- }
-
- public String getOriginalStoragePath() {
- return mOriginalStoragePath;
- }
-
- public String getStoragePath() {
- return mFile.getStoragePath();
- }
-
- public String getRemotePath() {
- return mFile.getRemotePath();
- }
-
- public String getMimeType() {
- return mFile.getMimeType();
- }
-
- public int getLocalBehaviour() {
- return mLocalBehaviour;
- }
-
- public void setRemoteFolderToBeCreated() {
- mRemoteFolderToBeCreated = true;
- }
-
- public boolean wasRenamed() {
- return mWasRenamed;
- }
-
- public int getCreatedBy() {
- return mCreatedBy;
- }
-
- public void setCreatedBy(int createdBy) {
- mCreatedBy = createdBy;
- if (createdBy < CREATED_BY_USER || CREATED_AS_CAMERA_UPLOAD_VIDEO < createdBy) {
- mCreatedBy = CREATED_BY_USER;
- }
- }
-
- public boolean isCameraUploadsPicture() {
- return mCreatedBy == CREATED_AS_CAMERA_UPLOAD_PICTURE;
- }
-
- public boolean isCameraUploadsVideo() {
- return mCreatedBy == CREATED_AS_CAMERA_UPLOAD_VIDEO;
- }
-
- public long getOCUploadId() {
- return mOCUploadId;
- }
-
- public void setOCUploadId(long id) {
- mOCUploadId = id;
- }
-
- public void addRenameUploadListener(OnRenameListener listener) {
- mRenameUploadListener = listener;
- }
-
- @Override
- protected RemoteOperationResult run(OwnCloudClient client) {
- mCancellationRequested.set(false);
- mUploadStarted.set(true);
- RemoteOperationResult result = null;
- File temporalFile = null, originalFile = new File(mOriginalStoragePath), expectedFile = null;
-
- try {
-
- /// check if the file continues existing before schedule the operation
- if (!originalFile.exists()) {
- Timber.d("%s not exists anymore", mOriginalStoragePath);
- return new RemoteOperationResult(ResultCode.LOCAL_FILE_NOT_FOUND);
- }
-
- /// check the existence of the parent folder for the file to upload
- String remoteParentPath = new File(getRemotePath()).getParent();
- remoteParentPath = remoteParentPath.endsWith(File.separator) ?
- remoteParentPath : remoteParentPath + File.separator;
- result = grantFolderExistence(remoteParentPath, client);
-
- if (!result.isSuccess()) {
- return result;
- }
-
- /// set parent local id in uploading file
- OCFile parent = getStorageManager().getFileByPath(remoteParentPath);
- mFile.setParentId(parent.getId());
-
- /// automatic rename of file to upload in case of name collision in server
- Timber.d("Checking name collision in server");
- if (!mForceOverwrite) {
- String remotePath = RemoteFileUtils.Companion.getAvailableRemotePath(client, mRemotePath);
- mWasRenamed = !mRemotePath.equals(remotePath);
- if (mWasRenamed) {
- createNewOCFile(remotePath);
- Timber.d("File renamed as %s", remotePath);
- }
- mRemotePath = remotePath;
- mRenameUploadListener.onRenameUpload();
- }
-
- if (mCancellationRequested.get()) {
- throw new OperationCancelledException();
- }
-
- String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile);
- expectedFile = new File(expectedPath);
-
- /// copy the file locally before uploading
- if (mLocalBehaviour == FileUploader.LEGACY_LOCAL_BEHAVIOUR_COPY &&
- !mOriginalStoragePath.equals(expectedPath)) {
-
- String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath();
- mFile.setStoragePath(temporalPath);
- temporalFile = new File(temporalPath);
-
- result = copy(originalFile, temporalFile);
- if (result != null) {
- return result;
- }
- }
-
- if (mCancellationRequested.get()) {
- throw new OperationCancelledException();
- }
-
- // Get the last modification date of the file from the file system
- Long timeStampLong = originalFile.lastModified() / 1000;
- String timeStamp = timeStampLong.toString();
-
- // Perform the upload
- result = uploadRemoteFile(client, temporalFile, originalFile, expectedPath, expectedFile, timeStamp);
-
- } catch (Exception e) {
- result = new RemoteOperationResult(e);
-
- } finally {
- mUploadStarted.set(false);
- if (temporalFile != null && !originalFile.equals(temporalFile)) {
- temporalFile.delete();
- }
- if (result == null) {
- result = new RemoteOperationResult(ResultCode.UNKNOWN_ERROR);
- }
- if (result.isSuccess()) {
- Timber.i("Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " +
- result.getLogMessage());
- } else {
- if (result.getException() != null) {
- if (result.isCancelled()) {
- Timber.w("Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage());
- } else {
- Timber.e(result.getException(), "Upload of " + mOriginalStoragePath + " to " + mRemotePath +
- ": " + result.getLogMessage());
- }
-
- } else {
- Timber.e("Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage());
- }
- }
- }
-
- if (result.isSuccess()) {
- saveUploadedFile(client);
-
- } else if (result.getCode() == ResultCode.SYNC_CONFLICT) {
- getStorageManager().saveConflict(mFile, mFile.getEtagInConflict());
- }
-
- return result;
- }
-
- /**
- * Upload file to server
- *
- * @param client
- * @param temporalFile file copied locally before uploading the file
- * @param originalFile local file to upload
- * @param expectedPath path in which the file should be uploaded
- * @param expectedFile resulting file
- * @param timeStamp
- * @return {@link RemoteOperationResult} representing the upload operation result
- */
- protected RemoteOperationResult uploadRemoteFile(OwnCloudClient client, File temporalFile, File originalFile,
- String expectedPath, File expectedFile, String timeStamp) {
- RemoteOperationResult result;
-
- try {
- mUploadOperation = new UploadFileFromFileSystemOperation(mFile.getStoragePath(), mFile.getRemotePath(),
- mFile.getMimeType(), timeStamp, mFile.getEtagInConflict());
-
- if (mCancellationRequested.get()) {
- throw new OperationCancelledException();
- }
-
- result = mUploadOperation.execute(client);
-
- /// move local temporal file or original file to its corresponding
- // location in the ownCloud local folder
- if (result.isSuccess()) {
- moveTemporalOriginalFiles(temporalFile, originalFile, expectedPath, expectedFile);
-
- } else if (result.getHttpCode() == HttpConstants.HTTP_PRECONDITION_FAILED) {
- result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
- }
- } catch (Exception e) {
- result = new RemoteOperationResult(e);
- }
-
- return result;
- }
-
- /**
- * Move local temporal file or original file to its corresponding location in the ownCloud local folder
- *
- * @param temporalFile file copied locally before uploading the file
- * @param originalFile local file to upload
- * @param expectedPath path in which the file should be uploaded
- * @param expectedFile resulting file
- * @param
- */
- protected void moveTemporalOriginalFiles(File temporalFile, File originalFile, String expectedPath,
- File expectedFile)
- throws IOException {
- if (mLocalBehaviour == FileUploader.LEGACY_LOCAL_BEHAVIOUR_FORGET) {
- String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath();
- if (mOriginalStoragePath.equals(temporalPath)) {
- // delete local file is was pre-copied in temporary folder (see .ui.helpers.UriUploader)
- temporalFile = new File(temporalPath);
- temporalFile.delete();
- }
- mFile.setStoragePath("");
-
- } else {
- mFile.setStoragePath(expectedPath);
-
- if (temporalFile != null) { // FileUploader.LOCAL_BEHAVIOUR_COPY
- move(temporalFile, expectedFile);
- } else { // FileUploader.LOCAL_BEHAVIOUR_MOVE
- move(originalFile, expectedFile);
- }
- }
- }
-
- /**
- * Checks the existence of the folder where the current file will be uploaded both
- * in the remote server and in the local database.
- *
- * If the upload is set to enforce the creation of the folder, the method tries to
- * create it both remote and locally.
- *
- * @param pathToGrant Full remote path whose existence will be granted.
- * @return An {@link OCFile} instance corresponding to the folder where the file
- * will be uploaded.
- */
- private RemoteOperationResult grantFolderExistence(String pathToGrant, OwnCloudClient client) {
- RemoteOperation checkPathExistenceOperation = new CheckPathExistenceRemoteOperation(pathToGrant, false);
- RemoteOperationResult result = checkPathExistenceOperation.execute(client);
- if (!result.isSuccess() && result.getCode() == ResultCode.FILE_NOT_FOUND && mRemoteFolderToBeCreated) {
- SyncOperation syncOp = new CreateFolderOperation(pathToGrant, true);
- result = syncOp.execute(client, getStorageManager());
- }
- if (result.isSuccess()) {
- OCFile parentDir = getStorageManager().getFileByPath(pathToGrant);
- if (parentDir == null) {
- parentDir = createLocalFolder(pathToGrant);
- }
- if (parentDir != null) {
- result = new RemoteOperationResult(ResultCode.OK);
- } else {
- result = new RemoteOperationResult(ResultCode.UNKNOWN_ERROR);
- }
- }
- return result;
- }
-
- private OCFile createLocalFolder(String remotePath) {
- String parentPath = new File(remotePath).getParent();
- parentPath = parentPath.endsWith(File.separator) ?
- parentPath : parentPath + File.separator;
- OCFile parent = getStorageManager().getFileByPath(parentPath);
- if (parent == null) {
- parent = createLocalFolder(parentPath);
- }
- if (parent != null) {
- OCFile createdFolder = new OCFile(remotePath, MIME_DIR, parent.getId(), parent.getOwner());
- getStorageManager().saveFile(createdFolder);
- return createdFolder;
- }
- return null;
- }
-
- /**
- * Create a new OCFile mFile with new remote path. This is required if forceOverwrite==false.
- * New file is stored as mFile, original as mOldFile.
- *
- * @param newRemotePath new remote path
- */
- private void createNewOCFile(String newRemotePath) {
- // a new OCFile instance must be created for a new remote path
- OCFile newFile = new OCFile(newRemotePath, mFile.getMimeType(), mFile.getParentId(), mFile.getOwner());
- newFile.setCreationTimestamp(mFile.getCreationTimestamp());
- newFile.setLength(mFile.getLength());
- newFile.setModificationTimestamp(mFile.getModificationTimestamp());
- newFile.setModifiedAtLastSyncForData(mFile.getModifiedAtLastSyncForData());
- newFile.setEtag(mFile.getEtag());
- // FIXME: 19/10/2020 : New_arch: Av.Offline
- // newFile.setAvailableOfflineStatus(mFile.getAvailableOfflineStatus());
- newFile.setLastSyncDateForProperties(mFile.getLastSyncDateForProperties());
- newFile.setLastSyncDateForData(mFile.getLastSyncDateForData());
- newFile.setStoragePath(mFile.getStoragePath());
- mOldFile = mFile;
- mFile = newFile;
- }
-
- /**
- * Allows to cancel the actual upload operation. If actual upload operating
- * is in progress it is cancelled, if upload preparation is being performed
- * upload will not take place.
- */
- public void cancel() {
- if (mUploadOperation == null) {
- if (mUploadStarted.get()) {
- Timber.d("Cancelling upload during upload preparations.");
- mCancellationRequested.set(true);
- } else {
- Timber.e("No upload in progress. This should not happen.");
- }
- } else {
- Timber.d("Cancelling upload during actual upload operation.");
- mUploadOperation.cancel();
- }
- }
-
- /**
- * As soon as this method return true, upload can be cancel via cancel().
- */
- public boolean isUploadInProgress() {
- return mUploadStarted.get();
-
- }
-
- /**
- * TODO rewrite with homogeneous fail handling, remove dependency on {@link RemoteOperationResult},
- * TODO use Exceptions instead
- *
- * @param sourceFile Source file to copy.
- * @param targetFile Target location to copy the file.
- * @return {@link RemoteOperationResult}
- * @throws IOException
- */
- private RemoteOperationResult copy(File sourceFile, File targetFile) throws IOException {
- Timber.d("Copying local file");
-
- RemoteOperationResult result = null;
-
- if (FileStorageUtils.getUsableSpace() < sourceFile.length()) {
- result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_FULL);
- return result; // error condition when the file should be copied
-
- } else {
- Timber.d("Creating temporal folder");
- File temporalParent = targetFile.getParentFile();
- temporalParent.mkdirs();
- if (!temporalParent.isDirectory()) {
- throw new IOException(
- "Unexpected error: parent directory could not be created");
- }
- Timber.d("Creating temporal file");
- targetFile.createNewFile();
- if (!targetFile.isFile()) {
- throw new IOException(
- "Unexpected error: target file could not be created");
- }
-
- Timber.d("Copying file contents");
- InputStream in = null;
- OutputStream out = null;
-
- try {
- if (!mOriginalStoragePath.equals(targetFile.getAbsolutePath())) {
- // In case document provider schema as 'content://'
- if (mOriginalStoragePath.startsWith(URI_CONTENT_SCHEME)) {
- Uri uri = Uri.parse(mOriginalStoragePath);
- in = mContext.getContentResolver().openInputStream(uri);
- } else {
- in = new FileInputStream(sourceFile);
- }
- out = new FileOutputStream(targetFile);
- int nRead;
- byte[] buf = new byte[4096];
- while (!mCancellationRequested.get() &&
- (nRead = in.read(buf)) > -1) {
- out.write(buf, 0, nRead);
- }
- out.flush();
-
- } // else: weird but possible situation, nothing to copy
-
- if (mCancellationRequested.get()) {
- result = new RemoteOperationResult(new OperationCancelledException());
- return result;
- }
-
- } catch (Exception e) {
- result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_COPIED);
- return result;
-
- } finally {
- try {
- if (in != null) {
- in.close();
- }
- } catch (Exception e) {
- Timber.e(e, "Weird exception while closing input stream for " + mOriginalStoragePath + " " +
- "(ignoring)");
- }
- try {
- if (out != null) {
- out.close();
- }
- } catch (Exception e) {
- Timber.e(e, "Weird exception while closing output stream for " + targetFile.getAbsolutePath() +
- " (ignoring)");
- }
- }
- }
- return result;
- }
-
- /**
- * TODO rewrite with homogeneous fail handling, remove dependency on {@link RemoteOperationResult},
- * TODO use Exceptions instead
- *
- * TODO refactor both this and 'copy' in a single method
- *
- * @param sourceFile Source file to move.
- * @param targetFile Target location to move the file.
- * @throws IOException
- */
- private void move(File sourceFile, File targetFile) throws IOException {
-
- if (!targetFile.equals(sourceFile)) {
- File expectedFolder = targetFile.getParentFile();
- expectedFolder.mkdirs();
-
- if (expectedFolder.isDirectory()) {
- if (!sourceFile.renameTo(targetFile)) {
- // try to copy and then delete
- targetFile.createNewFile();
- FileChannel inChannel = new FileInputStream(sourceFile).getChannel();
- FileChannel outChannel = new FileOutputStream(targetFile).getChannel();
- try {
- inChannel.transferTo(0, inChannel.size(), outChannel);
- sourceFile.delete();
- } catch (Exception e) {
- mFile.setStoragePath(""); // forget the local file
- // by now, treat this as a success; the file was uploaded
- // the best option could be show a warning message
- } finally {
- if (inChannel != null) {
- inChannel.close();
- }
- if (outChannel != null) {
- outChannel.close();
- }
- }
- }
-
- } else {
- mFile.setStoragePath("");
- }
- }
- }
-
- /**
- * Saves a OC File after a successful upload.
- *
- * A PROPFIND is necessary to keep the props in the local database
- * synchronized with the server, specially the modification time and Etag
- * (where available)
- */
- private void saveUploadedFile(OwnCloudClient client) {
- OCFile file = mFile;
- if (file.getFileExists()) {
- file = getStorageManager().getFileById(file.getId());
- }
- long syncDate = System.currentTimeMillis();
- file.setLastSyncDateForData(syncDate);
-
- // new PROPFIND to keep data consistent with server
- // in theory, should return the same we already have
- // TODO from the appropriate OC server version, get data from last PUT response headers, instead
- // TODO of a new PROPFIND; the latter may fail, specially for chunked uploads
- ReadRemoteFileOperation operation = new ReadRemoteFileOperation(getRemotePath());
- RemoteOperationResult result = operation.execute(client);
- if (result.isSuccess()) {
- updateOCFile(file, result.getData());
- file.setLastSyncDateForProperties(syncDate);
- } else {
- Timber.e("Error reading properties of file after successful upload; this is gonna hurt...");
- }
-
- if (mWasRenamed) {
- OCFile oldFile = getStorageManager().getFileByPath(mOldFile.getRemotePath());
- if (oldFile != null) {
- oldFile.setStoragePath(null);
- getStorageManager().saveFile(oldFile);
- getStorageManager().saveConflict(oldFile, null);
- }
- // else: it was just an automatic renaming due to a name
- // coincidence; nothing else is needed, the storagePath is right
- // in the instance returned by mCurrentUpload.getFile()
- }
- file.setNeedsToUpdateThumbnail(true);
- getStorageManager().saveFile(file);
- getStorageManager().saveConflict(file, null);
- }
-
- private void updateOCFile(OCFile file, RemoteFile remoteFile) {
- file.setCreationTimestamp(remoteFile.getCreationTimestamp());
- file.setLength(remoteFile.getLength());
- file.setMimeType(remoteFile.getMimeType());
- file.setModificationTimestamp(remoteFile.getModifiedTimestamp());
- file.setModifiedAtLastSyncForData(remoteFile.getModifiedTimestamp());
- file.setEtag(remoteFile.getEtag());
- file.setRemoteId(remoteFile.getRemoteId());
- }
-
- public interface OnRenameListener {
- void onRenameUpload();
- }
-}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/UploadPathActivity.java b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/UploadPathActivity.java
index 5e124281b7a..d79beefa9b4 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/UploadPathActivity.java
+++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/UploadPathActivity.java
@@ -26,7 +26,6 @@
import com.owncloud.android.domain.files.model.OCFile;
import com.owncloud.android.presentation.ui.files.filelist.MainFileListFragment;
import com.owncloud.android.ui.fragment.FileFragment;
-import com.owncloud.android.ui.fragment.OCFileListFragment;
public class UploadPathActivity extends FolderPickerActivity implements FileFragment.ContainerActivity,
OnClickListener, OnEnforceableRefreshListener {
diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/adapter/FileListListAdapter.java b/owncloudApp/src/main/java/com/owncloud/android/ui/adapter/FileListListAdapter.java
deleted file mode 100644
index 70a5b0df753..00000000000
--- a/owncloudApp/src/main/java/com/owncloud/android/ui/adapter/FileListListAdapter.java
+++ /dev/null
@@ -1,521 +0,0 @@
-/**
- * ownCloud Android client application
- *
- * @author Bartek Przybylski
- * @author Tobias Kaminsky
- * @author David A. Velasco
- * @author masensio
- * @author Christian Schabesberger
- * @author David González Verdugo
- * @author Shashvat Kedia
- * @author Abel García de Prada
- * @author John Kalimeris
- * @author David Crespo Ríos
- * Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2020 ownCloud GmbH.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package com.owncloud.android.ui.adapter;
-
-import android.accounts.Account;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.util.SparseBooleanArray;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.BaseAdapter;
-import android.widget.GridView;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.ListAdapter;
-import android.widget.TextView;
-
-import androidx.constraintlayout.widget.ConstraintLayout;
-import androidx.work.WorkManager;
-import com.owncloud.android.R;
-import com.owncloud.android.authentication.AccountUtils;
-import com.owncloud.android.data.preferences.datasources.SharedPreferencesProvider;
-import com.owncloud.android.data.preferences.datasources.implementation.SharedPreferencesProviderImpl;
-import com.owncloud.android.datamodel.FileDataStorageManager;
-import com.owncloud.android.datamodel.ThumbnailsCacheManager;
-import com.owncloud.android.db.PreferenceManager;
-import com.owncloud.android.domain.files.model.OCFile;
-import com.owncloud.android.extensions.WorkManagerExtKt;
-import com.owncloud.android.presentation.ui.settings.fragments.SettingsAdvancedFragment;
-import com.owncloud.android.services.OperationsService.OperationsServiceBinder;
-import com.owncloud.android.ui.activity.ComponentsGetter;
-import com.owncloud.android.utils.DisplayUtils;
-import com.owncloud.android.utils.FileStorageUtils;
-import com.owncloud.android.utils.MimetypeIconUtil;
-import com.owncloud.android.utils.PreferenceUtils;
-import com.owncloud.android.utils.SortFilesUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Vector;
-
-/**
- * This Adapter populates a ListView with all files and folders in an ownCloud
- * instance.
- */
-public class FileListListAdapter extends BaseAdapter implements ListAdapter {
-
- private final Context mContext;
- private List mImmutableFilesList = null; // List containing the database files, doesn't change with search
- private List mFiles = null; // List that can be changed when using search
- private final boolean mJustFolders;
- private final boolean mOnlyAvailableOffline;
- private final boolean mSharedByLinkFiles;
- private final boolean mFolderPicker;
-
- private FileDataStorageManager mStorageManager;
- private Account mAccount;
- private final ComponentsGetter mTransferServiceGetter;
-
- public FileListListAdapter(
- boolean justFolders,
- boolean onlyAvailableOffline,
- boolean sharedByLinkFiles,
- boolean folderPicker,
- Context context,
- ComponentsGetter transferServiceGetter
- ) {
- mJustFolders = justFolders;
- mOnlyAvailableOffline = onlyAvailableOffline;
- mSharedByLinkFiles = sharedByLinkFiles;
- mFolderPicker = folderPicker;
- mContext = context;
- mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);
-
- mTransferServiceGetter = transferServiceGetter;
-
- // Read sorting order, default to sort by name ascending
- FileStorageUtils.mSortOrderFileDisp = PreferenceManager.getSortOrder(mContext,
- FileStorageUtils.FILE_DISPLAY_SORT);
- FileStorageUtils.mSortAscendingFileDisp = PreferenceManager.getSortAscending(mContext,
- FileStorageUtils.FILE_DISPLAY_SORT);
-
- // initialise thumbnails cache on background thread
- new ThumbnailsCacheManager.InitDiskCacheTask().execute();
- }
-
- @Override
- public boolean areAllItemsEnabled() {
- return true;
- }
-
- @Override
- public boolean isEnabled(int position) {
- // Disable click for files when selecting a folder in copying and moving operations
- return !mFolderPicker || mFiles.get(position).isFolder();
- }
-
- @Override
- public int getCount() {
- return mFiles != null ? mFiles.size() : 0;
- }
-
- @Override
- public Object getItem(int position) {
- if (mFiles == null || mFiles.size() <= position) {
- return null;
- }
- return mFiles.get(position);
- }
-
- @Override
- public long getItemId(int position) {
- if (mFiles == null || mFiles.size() <= position) {
- return 0;
- }
- return mFiles.get(position).getId();
- }
-
- @Override
- public int getItemViewType(int position) {
- return 0;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
-
- View view = convertView;
- OCFile file = null;
- LayoutInflater inflator = (LayoutInflater) mContext
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- if (mFiles != null && mFiles.size() > position) {
- file = mFiles.get(position);
- }
-
- // Find out which layout should be displayed
- final ViewType viewType;
- if (parent instanceof GridView) {
- if (file != null && file.isImage()) {
- viewType = ViewType.GRID_IMAGE;
- } else {
- viewType = ViewType.GRID_ITEM;
- }
- } else {
- viewType = ViewType.LIST_ITEM;
- }
-
- // create view only if differs, otherwise reuse
- if (convertView == null || convertView.getTag() != viewType) {
- switch (viewType) {
- case GRID_IMAGE:
- view = inflator.inflate(R.layout.grid_image, parent, false);
- view.setTag(ViewType.GRID_IMAGE);
- break;
- case GRID_ITEM:
- view = inflator.inflate(R.layout.grid_item, parent, false);
- view.setTag(ViewType.GRID_ITEM);
- break;
- case LIST_ITEM:
- view = inflator.inflate(R.layout.item_file_list, parent, false);
- view.setTag(ViewType.LIST_ITEM);
- // Allow or disallow touches with other visible windows
- view.setFilterTouchesWhenObscured(
- PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(mContext)
- );
- break;
- }
- }
-
- if (file != null) {
- final ImageView localStateView = view.findViewById(R.id.localFileIndicator);
- final ImageView fileIcon = view.findViewById(R.id.thumbnail);
-
- fileIcon.setTag(file.getId());
- TextView fileName;
- String name = file.getFileName();
-
- final LinearLayout linearLayout = view.findViewById(R.id.ListItemLayout);
- if (linearLayout != null) {
- linearLayout.setContentDescription("LinearLayout-" + name);
-
- // Allow or disallow touches with other visible windows
- linearLayout.setFilterTouchesWhenObscured(
- PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(mContext)
- );
- }
-
- switch (viewType) {
- case LIST_ITEM:
- ConstraintLayout constraintLayout = view.findViewById(R.id.file_list_constraint_layout);
-
- // Allow or disallow touches with other visible windows
- constraintLayout.setFilterTouchesWhenObscured(
- PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(mContext));
-
- TextView fileSizeTV = view.findViewById(R.id.file_list_size);
- TextView lastModTV = view.findViewById(R.id.file_list_last_mod);
- fileSizeTV.setText(DisplayUtils.bytesToHumanReadable(file.getLength(), mContext));
- lastModTV.setText(DisplayUtils.getRelativeTimestamp(mContext, file.getModificationTimestamp()));
-
- if (mOnlyAvailableOffline || mSharedByLinkFiles) {
- TextView filePath = view.findViewById(R.id.file_list_path);
- filePath.setVisibility(View.VISIBLE);
- filePath.setText(file.getRemotePath());
- }
-
- case GRID_ITEM:
- // filename
- fileName = view.findViewById(R.id.Filename);
- name = file.getFileName();
- fileName.setText(name);
-
- case GRID_IMAGE:
- // sharedIcon
- ImageView sharedIconV = view.findViewById(R.id.sharedIcon);
- if (file.getSharedByLink()) {
- sharedIconV.setImageResource(R.drawable.ic_shared_by_link);
- sharedIconV.setVisibility(View.VISIBLE);
- sharedIconV.bringToFront();
- } else if (file.getSharedWithSharee() || file.isSharedWithMe()) {
- sharedIconV.setImageResource(R.drawable.shared_via_users);
- sharedIconV.setVisibility(View.VISIBLE);
- sharedIconV.bringToFront();
- } else {
- sharedIconV.setVisibility(View.GONE);
- }
- break;
- }
-
- // For all Views
- setIconPinAccordingToFilesLocalState(localStateView, file);
-
- final ImageView checkBoxV = view.findViewById(R.id.custom_checkbox);
- checkBoxV.setVisibility(View.GONE);
- view.setBackgroundColor(Color.WHITE);
-
- AbsListView parentList = (AbsListView) parent;
- if (parentList.getChoiceMode() != AbsListView.CHOICE_MODE_NONE &&
- parentList.getCheckedItemCount() > 0
- ) {
- if (parentList.isItemChecked(position)) {
- view.setBackgroundColor(mContext.getResources().getColor(
- R.color.selected_item_background));
- checkBoxV.setImageResource(
- R.drawable.ic_checkbox_marked);
- } else {
- view.setBackgroundColor(Color.WHITE);
- checkBoxV.setImageResource(
- R.drawable.ic_checkbox_blank_outline);
- }
- checkBoxV.setVisibility(View.VISIBLE);
- }
-
- if (file.isFolder()) {
- // Folder
- fileIcon.setImageResource(
- MimetypeIconUtil.getFolderTypeIconId(
- file.isSharedWithMe() || file.getSharedWithSharee(),
- file.getSharedByLink()));
- } else {
- // Set file icon depending on its mimetype. Ask for thumbnail later.
- fileIcon.setImageResource(MimetypeIconUtil.getFileTypeIconId(file.getMimeType(), file.getFileName()));
- if (file.getRemoteId() != null) {
- // Thumbnail in Cache?
- Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(file.getRemoteId());
- if (thumbnail != null) {
- fileIcon.setImageBitmap(thumbnail);
- }
- if (file.getNeedsToUpdateThumbnail()) {
- // generate new Thumbnail
- if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, fileIcon)) {
- final ThumbnailsCacheManager.ThumbnailGenerationTask task =
- new ThumbnailsCacheManager.ThumbnailGenerationTask(fileIcon, mAccount);
- final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable =
- new ThumbnailsCacheManager.AsyncThumbnailDrawable(
- mContext.getResources(),
- thumbnail,
- task
- );
- // If drawable is not visible, do not update it.
- if (asyncDrawable.getMinimumHeight() > 0 && asyncDrawable.getMinimumWidth() > 0) {
- fileIcon.setImageDrawable(asyncDrawable);
- }
- task.execute(file);
- }
- }
-
- if (file.getMimeType().equalsIgnoreCase("image/png")) {
- fileIcon.setBackgroundColor(mContext.getResources()
- .getColor(R.color.background_color));
- }
- }
-
- }
- }
- return view;
- }
-
- private void setIconPinAccordingToFilesLocalState(ImageView localStateView, OCFile file) {
- // local state
- localStateView.bringToFront();
- final WorkManager workManager = WorkManager.getInstance(mContext);
- final OperationsServiceBinder opsBinder =
- mTransferServiceGetter.getOperationsServiceBinder();
-
- localStateView.setVisibility(View.INVISIBLE); // default first
-
- if (opsBinder != null && opsBinder.isSynchronizing(mAccount, file)) {
- //syncing
- localStateView.setImageResource(R.drawable.sync_pin);
- localStateView.setVisibility(View.VISIBLE);
- } else if (WorkManagerExtKt.isDownloadPending(workManager, mAccount, file)) {
- // downloading
- localStateView.setImageResource(R.drawable.sync_pin);
- localStateView.setVisibility(View.VISIBLE);
- } else if (WorkManagerExtKt.isUploadPending(workManager, mAccount, file)) {
- // uploading
- localStateView.setImageResource(R.drawable.sync_pin);
- localStateView.setVisibility(View.VISIBLE);
- } else if (file.getEtagInConflict() != null) {
- // conflict
- localStateView.setImageResource(R.drawable.error_pin);
- localStateView.setVisibility(View.VISIBLE);
- } else {
- if (file.isAvailableLocally()) {
- localStateView.setVisibility(View.VISIBLE);
- localStateView.setImageResource(R.drawable.downloaded_pin);
- }
-
- // FIXME: 13/10/2020 : New_arch: Av.Offline
-// if (file.isAvailableOffline()) {
-// localStateView.setVisibility(View.VISIBLE);
-// localStateView.setImageResource(R.drawable.offline_available_pin);
-// }
- }
- }
-
- @Override
- public int getViewTypeCount() {
- return 1;
- }
-
- @Override
- public boolean hasStableIds() {
- return true;
- }
-
- @Override
- public boolean isEmpty() {
- return (mFiles == null || mFiles.isEmpty());
- }
-
- /**
- * Change the adapted directory for a new one
- *
- * @param folder New folder to adapt. Can be NULL, meaning
- * "no content to adapt".
- * @param updatedStorageManager Optional updated storage manager; used to replace
- * mStorageManager if is different (and not NULL)
- */
- public void swapDirectory(OCFile folder, FileDataStorageManager updatedStorageManager) {
- if (updatedStorageManager != null && updatedStorageManager != mStorageManager) {
- mStorageManager = updatedStorageManager;
- mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);
- }
-
- boolean isRootFolder = folder.equals(updatedStorageManager.getFileByPath(OCFile.ROOT_PATH));
-
- if (mStorageManager != null) {
- // FIXME: 13/10/2020 : New_arch: Av.Offline
- if (mOnlyAvailableOffline && (isRootFolder)){ // || !folder.isAvailableOffline())) {
- mImmutableFilesList = updatedStorageManager.getAvailableOfflineFilesFromCurrentAccount();
- } else if (mSharedByLinkFiles && isRootFolder) {
- mImmutableFilesList = updatedStorageManager.sharedByLinkFilesFromCurrentAccount();
- } else {
- mImmutableFilesList = mStorageManager.getFolderContent(folder);
- }
-
- mImmutableFilesList = new SortFilesUtils().sortFiles(mImmutableFilesList, FileStorageUtils.mSortOrderFileDisp,
- FileStorageUtils.mSortAscendingFileDisp);
-
- mFiles = mImmutableFilesList;
-
- if (mJustFolders) {
- mFiles = getFolders(mFiles);
- }
- } else {
- mFiles = null;
- }
-
- SharedPreferencesProvider sharedPreferencesProvider = new SharedPreferencesProviderImpl(mContext);
- boolean showHiddenFiles = sharedPreferencesProvider.getBoolean(SettingsAdvancedFragment.PREF_SHOW_HIDDEN_FILES, false);
-
- if (!showHiddenFiles) {
- filterByHiddenFiles();
- }
-
- notifyDataSetChanged();
- }
-
- /**
- * Filter for getting only the folders
- *
- * @param files Collection of files to filter
- * @return Folders in the input
- */
- private List getFolders(List files) {
- List ret = new Vector<>();
- OCFile current;
- for (int i = 0; i < files.size(); i++) {
- current = files.get(i);
- if (current.isFolder()) {
- ret.add(current);
- }
- }
- return ret;
- }
-
- public void setSortOrder(Integer order, boolean ascending) {
-
- PreferenceManager.setSortOrder(order, mContext, FileStorageUtils.FILE_DISPLAY_SORT);
- PreferenceManager.setSortAscending(ascending, mContext, FileStorageUtils.FILE_DISPLAY_SORT);
-
- FileStorageUtils.mSortOrderFileDisp = order;
- FileStorageUtils.mSortAscendingFileDisp = ascending;
-
- mFiles = new SortFilesUtils().sortFiles(mFiles, FileStorageUtils.mSortOrderFileDisp,
- FileStorageUtils.mSortAscendingFileDisp);
- notifyDataSetChanged();
- }
-
- public ArrayList getCheckedItems(AbsListView parentList) {
- SparseBooleanArray checkedPositions = parentList.getCheckedItemPositions();
- ArrayList files = new ArrayList<>();
- Object item;
- for (int i = 0; i < checkedPositions.size(); i++) {
- if (checkedPositions.valueAt(i)) {
- item = getItem(checkedPositions.keyAt(i));
- if (item != null) {
- files.add((OCFile) item);
- }
- }
- }
- return files;
- }
-
- public void filterBySearch(String query) {
- clearFilterBySearch();
-
- List filteredList = new ArrayList<>();
-
- // Gather files matching the query
- for (OCFile fileToAdd : mFiles) {
- final String nameOfTheFileToAdd = fileToAdd.getFileName().toLowerCase();
- if (nameOfTheFileToAdd.contains(query)) {
- filteredList.add(fileToAdd);
- }
- }
-
- // Remove not matching files from the filelist
- for (int i = mFiles.size() - 1; i >= 0; i--) {
- if (!filteredList.contains(mFiles.get(i))) {
- mFiles.remove(i);
- }
- }
-
- // Add matching files to the filelist
- for (int i = 0; i < filteredList.size(); i++) {
- if (!mFiles.contains(filteredList.get(i))) {
- mFiles.add(i, filteredList.get(i));
- }
- }
-
- notifyDataSetChanged();
- }
-
- public void clearFilterBySearch() {
- mFiles = new ArrayList(mImmutableFilesList);
- notifyDataSetChanged();
- }
-
- public void filterByHiddenFiles() {
- for (int i = 0; i < mFiles.size(); i++) {
- if (mFiles.get(i).getFileName().startsWith(".")) {
- mFiles.remove(i);
- i--; // Otherwise it will skip the element after the removed index
- }
- }
- }
-
- private enum ViewType {LIST_ITEM, GRID_IMAGE, GRID_ITEM}
-}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/errorhandling/ErrorMessageAdapter.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/errorhandling/ErrorMessageAdapter.kt
index b2daa924cee..93392eca03d 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/ui/errorhandling/ErrorMessageAdapter.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/ui/errorhandling/ErrorMessageAdapter.kt
@@ -25,14 +25,18 @@ package com.owncloud.android.ui.errorhandling
import android.content.res.Resources
import com.owncloud.android.R
+import com.owncloud.android.domain.exceptions.FileNotFoundException
+import com.owncloud.android.domain.exceptions.ForbiddenException
+import com.owncloud.android.domain.exceptions.InvalidCharacterException
+import com.owncloud.android.domain.exceptions.LocalStorageFullException
+import com.owncloud.android.domain.exceptions.LocalStorageNotCopiedException
+import com.owncloud.android.domain.exceptions.QuotaExceededException
import com.owncloud.android.extensions.parseError
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
-import com.owncloud.android.operations.CreateFolderOperation
import com.owncloud.android.operations.SynchronizeFileOperation
import com.owncloud.android.operations.SynchronizeFolderOperation
-import com.owncloud.android.operations.UploadFileOperation
import com.owncloud.android.ui.errorhandling.TransferOperation.Download
import java.io.File
import java.net.SocketTimeoutException
@@ -88,6 +92,10 @@ class ErrorMessageAdapter {
File(transferOperation.downloadPath).name
)
}
+ is TransferOperation.Upload -> formatter.format(
+ R.string.uploader_upload_succeeded_content_single,
+ transferOperation.fileName
+ )
}
} else {
val genericMessage = when (transferOperation) {
@@ -95,11 +103,49 @@ class ErrorMessageAdapter {
R.string.downloader_download_failed_content,
File(transferOperation.downloadPath).name
)
+ is TransferOperation.Upload -> {
+ getMessageForFailedUpload(formatter, throwable, transferOperation)
+ }
}
return throwable.parseError(genericMessage, resources, true).toString()
}
}
+ private fun getMessageForFailedUpload(
+ formatter: Formatter,
+ throwable: Throwable,
+ transferOperation: TransferOperation.Upload
+ ): String {
+ return when (throwable) {
+ is LocalStorageFullException -> formatter.format(
+ R.string.error__upload__local_file_not_copied,
+ transferOperation.fileName, R.string.app_name
+ )
+ is LocalStorageNotCopiedException -> formatter.format(
+ R.string.error__upload__local_file_not_copied,
+ transferOperation.fileName, R.string.app_name
+ )
+ is ForbiddenException -> {
+ formatter.format(
+ R.string.forbidden_permissions,
+ R.string.uploader_upload_forbidden_permissions
+ )
+ }
+ is InvalidCharacterException -> {
+ formatter.format(
+ R.string.filename_forbidden_characters_from_server
+ )
+ }
+ is QuotaExceededException ->
+ formatter.format(R.string.failed_upload_quota_exceeded_text)
+ is FileNotFoundException -> {
+ formatter.format(R.string.uploads_view_upload_status_failed_folder_error)
+ }
+ else -> formatter.format(R.string.uploader_upload_failed_content_single, transferOperation.fileName)
+
+ }
+ }
+
/**
* Return an internationalized user message corresponding to an operation result
* and the operation performed.
@@ -115,35 +161,14 @@ class ErrorMessageAdapter {
resources: Resources
): String {
val formatter = Formatter(resources)
- if (result.isSuccess) {
- when (operation) {
- is UploadFileOperation -> return formatter.format(
- R.string.uploader_upload_succeeded_content_single,
- operation.fileName
- )
- }
- }
if (operation is SynchronizeFileOperation && !operation.transferWasRequested()) {
return formatter.format(R.string.sync_file_nothing_to_do_msg)
}
return when (result.code) {
- ResultCode.LOCAL_STORAGE_FULL -> formatter.format(
- R.string.error__upload__local_file_not_copied,
- (operation as UploadFileOperation).fileName, R.string.app_name
- )
- ResultCode.LOCAL_STORAGE_NOT_COPIED -> formatter.format(
- R.string.error__upload__local_file_not_copied,
- (operation as UploadFileOperation).fileName, R.string.app_name
- )
ResultCode.FORBIDDEN -> {
- if (operation is UploadFileOperation) formatter.format(
- R.string.forbidden_permissions,
- R.string.uploader_upload_forbidden_permissions
- )
- if (operation is CreateFolderOperation) formatter.forbidden(R.string.forbidden_permissions_create)
- else formatter.format(
+ formatter.format(
R.string.filename_forbidden_characters_from_server
)
}
@@ -152,8 +177,6 @@ class ErrorMessageAdapter {
ResultCode.QUOTA_EXCEEDED ->
formatter.format(R.string.failed_upload_quota_exceeded_text)
ResultCode.FILE_NOT_FOUND -> {
- if (operation is UploadFileOperation)
- formatter.format(R.string.uploads_view_upload_status_failed_folder_error)
if (operation is SynchronizeFolderOperation)
formatter.format(
R.string.sync_current_folder_was_removed,
@@ -232,8 +255,6 @@ class ErrorMessageAdapter {
val formatter = Formatter(res)
return when (operation) {
- is UploadFileOperation -> formatter.format(R.string.uploader_upload_failed_content_single, operation.fileName)
- is CreateFolderOperation -> formatter.format(R.string.create_dir_fail_msg)
is SynchronizeFolderOperation -> formatter.format(
R.string.sync_folder_failed_content,
File(operation.folderPath).name
diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/errorhandling/TransferOperation.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/errorhandling/TransferOperation.kt
index dfb8fa2d3d0..5748f740016 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/ui/errorhandling/TransferOperation.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/ui/errorhandling/TransferOperation.kt
@@ -18,6 +18,7 @@
*/
package com.owncloud.android.ui.errorhandling
-sealed class TransferOperation {
- data class Download(val downloadPath: String) : TransferOperation()
+sealed interface TransferOperation {
+ data class Download(val downloadPath: String) : TransferOperation
+ data class Upload(val fileName: String) : TransferOperation
}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/fragment/ExpandableListFragment.java b/owncloudApp/src/main/java/com/owncloud/android/ui/fragment/ExpandableListFragment.java
index 1bdce9a1455..a760584ceee 100755
--- a/owncloudApp/src/main/java/com/owncloud/android/ui/fragment/ExpandableListFragment.java
+++ b/owncloudApp/src/main/java/com/owncloud/android/ui/fragment/ExpandableListFragment.java
@@ -30,12 +30,13 @@
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnChildClickListener;
+import androidx.core.content.res.ResourcesCompat;
import com.owncloud.android.R;
import com.owncloud.android.utils.PreferenceUtils;
import timber.log.Timber;
/**
- * Extending ExtendedListFragment. This allows dividing list in groups.
+ * Extending ExtendedListFragment. This allows dividing list in groups.
*/
public class ExpandableListFragment extends ExtendedListFragment implements OnChildClickListener {
@@ -65,7 +66,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa
mList = v.findViewById(R.id.list_root);
mList.setOnChildClickListener(this);
- mList.setDivider(getResources().getDrawable(R.drawable.uploader_list_separator));
+ mList.setDivider(ResourcesCompat.getDrawable(getResources(), R.drawable.uploader_list_separator, null));
mList.setDividerHeight(1);
// if (savedInstanceState != null) {
diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java b/owncloudApp/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java
index b752824fb46..7bd6ba2c408 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java
+++ b/owncloudApp/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java
@@ -23,27 +23,17 @@
package com.owncloud.android.ui.fragment;
import android.os.Bundle;
-import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
-import android.widget.GridView;
-import android.widget.ListAdapter;
-import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
-import com.getbase.floatingactionbutton.FloatingActionButton;
-import com.getbase.floatingactionbutton.FloatingActionsMenu;
import com.owncloud.android.R;
-import com.owncloud.android.ui.ExtendedListView;
import com.owncloud.android.ui.activity.OnEnforceableRefreshListener;
-import com.owncloud.android.utils.PreferenceUtils;
-import third_parties.in.srain.cube.GridViewWithHeaderAndFooter;
import timber.log.Timber;
import java.util.ArrayList;
@@ -58,26 +48,12 @@ public class ExtendedListFragment extends Fragment
private static final String KEY_TOPS = "TOPS";
private static final String KEY_HEIGHT_CELL = "HEIGHT_CELL";
private static final String KEY_EMPTY_LIST_MESSAGE = "EMPTY_LIST_MESSAGE";
- private static final String KEY_IS_GRID_VISIBLE = "IS_GRID_VISIBLE";
-
- static final String ARG_JUST_FOLDERS = ExtendedListFragment.class.getCanonicalName() + ".JUST_FOLDERS";
- static final String ARG_LIST_FILE_OPTION = ExtendedListFragment.class.getCanonicalName() +
- ".LIST_FILE_OPTION";
- static final String ARG_PICKING_A_FOLDER = ExtendedListFragment.class.getCanonicalName() +
- ".ARG_PICKING_A_FOLDER";
-
- private ProgressBar mProgressBar;
- private View mShadowView;
SwipeRefreshLayout mRefreshListLayout;
private SwipeRefreshLayout mRefreshGridLayout;
SwipeRefreshLayout mRefreshEmptyLayout;
TextView mEmptyListMessage;
- private FloatingActionsMenu mFabMain;
- private FloatingActionButton mFabUpload;
- private FloatingActionButton mFabMkdir;
-
// Save the state of the scroll in browsing
private ArrayList mIndexes;
private ArrayList mFirstPositions;
@@ -87,120 +63,11 @@ public class ExtendedListFragment extends Fragment
private SwipeRefreshLayout.OnRefreshListener mOnRefreshListener = null;
AbsListView mCurrentListView;
- private ExtendedListView mListView;
- private View mListFooterView;
- private GridViewWithHeaderAndFooter mGridView;
- private View mGridFooterView;
-
- private ListAdapter mAdapter;
-
- void setListAdapter(ListAdapter listAdapter) {
- mAdapter = listAdapter;
- mCurrentListView.setAdapter(listAdapter);
- mCurrentListView.invalidateViews();
- }
protected AbsListView getListView() {
return mCurrentListView;
}
- FloatingActionButton getFabUpload() {
- return mFabUpload;
- }
-
- FloatingActionButton getFabMkdir() {
- return mFabMkdir;
- }
-
- public FloatingActionsMenu getFabMain() {
- return mFabMain;
- }
-
- void switchToGridView() {
- if (!isGridEnabled()) {
- mListView.setAdapter(null);
- mRefreshListLayout.setVisibility(View.GONE);
- mRefreshGridLayout.setVisibility(View.VISIBLE);
- mCurrentListView = mGridView;
- setListAdapter(mAdapter);
- }
- }
-
- void switchToListView() {
- if (isGridEnabled()) {
- mGridView.setAdapter(null);
- mRefreshGridLayout.setVisibility(View.GONE);
- mRefreshListLayout.setVisibility(View.VISIBLE);
- mCurrentListView = mListView;
- setListAdapter(mAdapter);
- }
- }
-
- public boolean isGridEnabled() {
- return (mCurrentListView == mGridView);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- Timber.v("onCreateView");
- View v = inflater.inflate(R.layout.list_fragment, null);
-
- mProgressBar = v.findViewById(R.id.syncProgressBar);
- mShadowView = v.findViewById(R.id.shadow_view);
-
- mListView = v.findViewById(R.id.list_root);
- mListView.setOnItemClickListener(this);
- mListFooterView = inflater.inflate(R.layout.list_footer, null, false);
-
- mListFooterView.setFilterTouchesWhenObscured(
- PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(getContext())
- );
-
- mGridView = v.findViewById(R.id.grid_root);
- mGridView.setNumColumns(GridView.AUTO_FIT);
- mGridView.setOnItemClickListener(this);
-
- mGridFooterView = inflater.inflate(R.layout.list_footer, null, false);
-
- mGridFooterView.setFilterTouchesWhenObscured(
- PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(getContext())
- );
-
- // Pull-down to refresh layout
- mRefreshListLayout = v.findViewById(R.id.swipe_containing_list);
- mRefreshGridLayout = v.findViewById(R.id.swipe_containing_grid);
- mRefreshEmptyLayout = v.findViewById(R.id.swipe_containing_empty);
- mEmptyListMessage = v.findViewById(R.id.empty_list_view);
-
- onCreateSwipeToRefresh(mRefreshListLayout);
- onCreateSwipeToRefresh(mRefreshGridLayout);
- onCreateSwipeToRefresh(mRefreshEmptyLayout);
-
- mListView.setEmptyView(mRefreshEmptyLayout);
- mGridView.setEmptyView(mRefreshEmptyLayout);
-
- mFabMain = v.findViewById(R.id.fab_main);
- mFabUpload = v.findViewById(R.id.fab_upload);
- mFabMkdir = v.findViewById(R.id.fab_mkdir);
-
- mCurrentListView = mListView; // list by default
- if (savedInstanceState != null) {
- if (savedInstanceState.getBoolean(KEY_IS_GRID_VISIBLE, false)) {
- switchToGridView();
- }
- int referencePosition = savedInstanceState.getInt(KEY_SAVED_LIST_POSITION);
- if (isGridEnabled()) {
- Timber.v("Setting grid position %s", referencePosition);
- mGridView.setSelection(referencePosition);
- } else {
- Timber.v("Setting and centering around list position %s", referencePosition);
- mListView.setAndCenterSelection(referencePosition);
- }
- }
-
- return v;
- }
-
/**
* {@inheritDoc}
*/
@@ -226,7 +93,6 @@ public void onActivityCreated(Bundle savedInstanceState) {
public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
Timber.v("onSaveInstanceState()");
- savedInstanceState.putBoolean(KEY_IS_GRID_VISIBLE, isGridEnabled());
savedInstanceState.putInt(KEY_SAVED_LIST_POSITION, getReferencePosition());
savedInstanceState.putIntegerArrayList(KEY_INDEXES, mIndexes);
savedInstanceState.putIntegerArrayList(KEY_FIRST_POSITIONS, mFirstPositions);
@@ -255,58 +121,6 @@ protected int getReferencePosition() {
}
}
- /*
- * Restore index and position
- */
- protected void restoreIndexAndTopPosition() {
- if (mIndexes.size() > 0) {
- // needs to be checked; not every browse-up had a browse-down before
-
- int index = mIndexes.remove(mIndexes.size() - 1);
- final int firstPosition = mFirstPositions.remove(mFirstPositions.size() - 1);
- int top = mTops.remove(mTops.size() - 1);
-
- Timber.v("Setting selection to position: " + firstPosition + "; top: " + top + "; index: " + index);
-
- if (mCurrentListView == mListView) {
- if (mHeightCell * index <= mListView.getHeight()) {
- mListView.setSelectionFromTop(firstPosition, top);
- } else {
- mListView.setSelectionFromTop(index, 0);
- }
-
- } else {
- if (mHeightCell * index <= mGridView.getHeight()) {
- mGridView.setSelection(firstPosition);
- //mGridView.smoothScrollToPosition(firstPosition);
- } else {
- mGridView.setSelection(index);
- //mGridView.smoothScrollToPosition(index);
- }
- }
-
- }
- }
-
- /*
- * Save index and top position
- */
- protected void saveIndexAndTopPosition(int index) {
-
- mIndexes.add(index);
-
- int firstPosition = mCurrentListView.getFirstVisiblePosition();
- mFirstPositions.add(firstPosition);
-
- View view = mCurrentListView.getChildAt(0);
- int top = (view == null) ? 0 : view.getTop();
-
- mTops.add(top);
-
- // Save the height of a cell
- mHeightCell = (view == null || mHeightCell != 0) ? mHeightCell : view.getHeight();
- }
-
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
// to be @overridden
@@ -327,36 +141,6 @@ public void setOnRefreshListener(OnEnforceableRefreshListener listener) {
mOnRefreshListener = listener;
}
- /**
- * Disables swipe gesture.
- *
- * Sets the 'enabled' state of the refresh layouts contained in the fragment.
- *
- * When 'false' is set, prevents user gestures but keeps the option to refresh programatically,
- *
- * @param enabled Desired state for capturing swipe gesture.
- */
- public void setSwipeEnabled(boolean enabled) {
- mRefreshListLayout.setEnabled(enabled);
- mRefreshGridLayout.setEnabled(enabled);
- mRefreshEmptyLayout.setEnabled(enabled);
- }
-
- /**
- * Sets the 'visibility' state of the FAB contained in the fragment.
- *
- * When 'false' is set, FAB visibility is set to View.GONE programatically,
- *
- * @param enabled Desired visibility for the FAB.
- */
- public void setFabEnabled(boolean enabled) {
- if (enabled) {
- mFabMain.setVisibility(View.VISIBLE);
- } else {
- mFabMain.setVisibility(View.GONE);
- }
- }
-
/**
* Set message for empty list view
*/
@@ -393,90 +177,4 @@ public void onRefresh(boolean ignoreETag) {
mOnRefreshListener.onRefresh();
}
}
-
- protected void setChoiceMode(int choiceMode) {
- mListView.setChoiceMode(choiceMode);
- mGridView.setChoiceMode(choiceMode);
- }
-
- protected void setMultiChoiceModeListener(AbsListView.MultiChoiceModeListener listener) {
- mListView.setMultiChoiceModeListener(listener);
- mGridView.setMultiChoiceModeListener(listener);
- }
-
- /**
- * TODO doc
- * To be called before setAdapter, or GridViewWithHeaderAndFooter will throw an exception
- *
- * @param enabled
- */
- protected void setFooterEnabled(boolean enabled) {
- if (enabled) {
- if (mGridView.getFooterViewCount() == 0) {
- if (mGridFooterView.getParent() != null) {
- ((ViewGroup) mGridFooterView.getParent()).removeView(mGridFooterView);
- }
- try {
- mGridView.addFooterView(mGridFooterView, null, false);
- } catch (IllegalStateException ie) {
- Timber.w("Could not add footer to grid view, because it exists");
- }
- }
- mGridFooterView.invalidate();
-
- if (mListView.getFooterViewsCount() == 0) {
- if (mListFooterView.getParent() != null) {
- ((ViewGroup) mListFooterView.getParent()).removeView(mListFooterView);
- }
- mListView.addFooterView(mListFooterView, null, false);
- }
- mListFooterView.invalidate();
-
- } else {
- mGridView.removeFooterView(mGridFooterView);
- mListView.removeFooterView(mListFooterView);
- }
- }
-
- public ProgressBar getProgressBar() {
- return mProgressBar;
- }
-
- public View getShadowView() {
- return mShadowView;
- }
-
- /**
- * TODO doc
- *
- * @param text
- */
- protected void setFooterText(String text) {
- if (text != null && text.length() > 0) {
- ((TextView) mListFooterView.findViewById(R.id.footerText)).setText(text);
- ((TextView) mGridFooterView.findViewById(R.id.footerText)).setText(text);
- setFooterEnabled(true);
-
- } else {
- setFooterEnabled(false);
- }
- }
-
- public void setProgressBarAsIndeterminate(boolean indeterminate) {
- Timber.d("Setting progress visibility to %s", indeterminate);
- mShadowView.setVisibility(View.GONE);
- mProgressBar.setVisibility(View.VISIBLE);
- mProgressBar.setIndeterminate(indeterminate);
- mProgressBar.postInvalidate();
- }
-
- boolean isShowingJustFolders() {
- Bundle args = getArguments();
- return ((args != null) && args.getBoolean(ARG_JUST_FOLDERS, false));
- }
-
- boolean isPickingAFolder() {
- Bundle args = getArguments();
- return ((args != null) && args.getBoolean(ARG_PICKING_A_FOLDER, false));
- }
}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java b/owncloudApp/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java
deleted file mode 100644
index afca3795a8f..00000000000
--- a/owncloudApp/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java
+++ /dev/null
@@ -1,1352 +0,0 @@
-/**
- * ownCloud Android client application
- *
- * @author Bartek Przybylski
- * @author masensio
- * @author David A. Velasco
- * @author Christian Schabesberger
- * @author David González Verdugo
- * @author Shashvat Kedia
- * @author Abel García de Prada
- * @author David Crespo Rios
- * Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2022 ownCloud GmbH.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package com.owncloud.android.ui.fragment;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.preference.PreferenceManager;
-import android.util.SparseBooleanArray;
-import android.view.ActionMode;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.widget.SearchView;
-import androidx.coordinatorlayout.widget.CoordinatorLayout;
-import androidx.drawerlayout.widget.DrawerLayout;
-import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
-import androidx.work.WorkManager;
-import com.google.android.material.bottomsheet.BottomSheetBehavior;
-import com.google.android.material.bottomsheet.BottomSheetDialog;
-import com.google.android.material.snackbar.Snackbar;
-import com.owncloud.android.R;
-import com.owncloud.android.authentication.AccountUtils;
-import com.owncloud.android.datamodel.FileDataStorageManager;
-import com.owncloud.android.extensions.DialogExtKt;
-import com.owncloud.android.extensions.FragmentExtKt;
-import com.owncloud.android.domain.files.model.OCFile;
-import com.owncloud.android.extensions.ThrowableExtKt;
-import com.owncloud.android.extensions.WorkManagerExtKt;
-import com.owncloud.android.files.FileMenuFilter;
-import com.owncloud.android.lib.resources.status.OwnCloudVersion;
-import com.owncloud.android.presentation.UIResult;
-import com.owncloud.android.presentation.ui.common.BottomSheetFragmentItemView;
-import com.owncloud.android.presentation.ui.files.SortBottomSheetFragment;
-import com.owncloud.android.presentation.ui.files.SortOptionsView;
-import com.owncloud.android.presentation.ui.files.SortOrder;
-import com.owncloud.android.presentation.ui.files.SortType;
-import com.owncloud.android.presentation.ui.files.ViewType;
-import com.owncloud.android.presentation.ui.files.createfolder.CreateFolderDialogFragment;
-import com.owncloud.android.presentation.viewmodels.files.FilesViewModel;
-import com.owncloud.android.ui.activity.FileActivity;
-import com.owncloud.android.ui.activity.FileDisplayActivity;
-import com.owncloud.android.domain.files.model.FileListOption;
-import com.owncloud.android.ui.activity.FolderPickerActivity;
-import com.owncloud.android.ui.activity.OnEnforceableRefreshListener;
-import com.owncloud.android.ui.adapter.FileListListAdapter;
-import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
-import com.owncloud.android.presentation.ui.files.removefile.RemoveFilesDialogFragment;
-import com.owncloud.android.ui.dialog.RenameFileDialogFragment;
-import com.owncloud.android.ui.helpers.SparseBooleanArrayParcelable;
-import com.owncloud.android.ui.preview.PreviewAudioFragment;
-import com.owncloud.android.ui.preview.PreviewImageFragment;
-import com.owncloud.android.ui.preview.PreviewTextFragment;
-import com.owncloud.android.ui.preview.PreviewVideoFragment;
-import com.owncloud.android.utils.FileStorageUtils;
-import com.owncloud.android.utils.PreferenceUtils;
-import kotlin.Unit;
-import org.jetbrains.annotations.NotNull;
-import timber.log.Timber;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import static org.koin.java.KoinJavaComponent.get;
-
-/**
- * A Fragment that lists all files and folders in a given path.
- *
- * TODO refactor to get rid of direct dependency on FileDisplayActivity
- */
-public class OCFileListFragment extends ExtendedListFragment implements
- SearchView.OnQueryTextListener, View.OnFocusChangeListener, SortOptionsView.SortOptionsListener,
- SortBottomSheetFragment.SortDialogListener, SortOptionsView.CreateFolderListener, CreateFolderDialogFragment.CreateFolderListener {
-
- private static final String MY_PACKAGE = OCFileListFragment.class.getPackage() != null ?
- OCFileListFragment.class.getPackage().getName() : "com.owncloud.android.ui.fragment";
-
- private final static String ARG_ALLOW_CONTEXTUAL_MODE = MY_PACKAGE + ".ALLOW_CONTEXTUAL";
- private final static String ARG_HIDE_FAB = MY_PACKAGE + ".HIDE_FAB";
-
- private static final String KEY_FILE = MY_PACKAGE + ".extra.FILE";
- private static final String KEY_FILE_LIST_OPTION = "FILE_LIST_OPTION";
- private static final String KEY_FAB_EVER_CLICKED = "FAB_EVER_CLICKED";
-
- private static final String GRID_IS_PREFERED_PREFERENCE = "gridIsPrefered";
-
- private static String DIALOG_CREATE_FOLDER = "DIALOG_CREATE_FOLDER";
-
- private final String ALL_FILES_SAF_REGEX = "*/*";
-
- private FileFragment.ContainerActivity mContainerActivity;
-
- private FileListOption mFileListOption = null;
- private OCFile mFile = null;
- private FileListListAdapter mFileListAdapter;
-
- private boolean mEnableSelectAll = true;
-
- private int mStatusBarColorActionMode;
- private int mStatusBarColor;
-
- private boolean mHideFab = true;
-
- private boolean miniFabClicked = false;
- private ActionMode mActiveActionMode;
- private OCFileListFragment.MultiChoiceModeListener mMultiChoiceModeListener;
-
- private SearchView mSearchView;
-
- private SortOptionsView mSortOptionsView;
-
- /**
- * Public factory method to create new {@link OCFileListFragment} instances.
- *
- * @param justFolders When 'true', only folders will be shown to the user, not files.
- * @param fileListOption File list option to show. All files by default.
- * @param pickingAFolder When 'true', only folders will be clickable when selecting a folder when copying or
- * moving files or configuring upload path for camera uploads
- * @param hideFAB When 'true', floating action button is hidden.
- * @param allowContextualMode When 'true', contextual action mode is enabled long-pressing an item.
- * @return New fragment with arguments set.
- */
- public static OCFileListFragment newInstance(
- boolean justFolders,
- FileListOption fileListOption,
- boolean pickingAFolder,
- boolean hideFAB,
- boolean allowContextualMode
- ) {
- OCFileListFragment frag = new OCFileListFragment();
- Bundle args = new Bundle();
- args.putBoolean(ARG_JUST_FOLDERS, justFolders);
- if (fileListOption == null) {
- fileListOption = FileListOption.ALL_FILES;
- }
- if (!fileListOption.isAllFiles()) {
- hideFAB = true;
- }
- args.putParcelable(ARG_LIST_FILE_OPTION, fileListOption);
- args.putBoolean(ARG_PICKING_A_FOLDER, pickingAFolder);
- args.putBoolean(ARG_HIDE_FAB, hideFAB);
- args.putBoolean(ARG_ALLOW_CONTEXTUAL_MODE, allowContextualMode);
- frag.setArguments(args);
- return frag;
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setHasOptionsMenu(true);
- mStatusBarColorActionMode = getResources().getColor(R.color.action_mode_status_bar_background);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onAttach(@NonNull Context context) {
- super.onAttach(context);
- Timber.v("onAttach");
- try {
- mContainerActivity = (FileFragment.ContainerActivity) context;
-
- } catch (ClassCastException e) {
- throw new ClassCastException(context.toString() + " must implement " +
- FileFragment.ContainerActivity.class.getSimpleName());
- }
- try {
- setOnRefreshListener((OnEnforceableRefreshListener) context);
-
- } catch (ClassCastException e) {
- throw new ClassCastException(context.toString() + " must implement " +
- SwipeRefreshLayout.OnRefreshListener.class.getSimpleName());
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- Timber.i("onCreateView() start");
- View v = super.onCreateView(inflater, container, savedInstanceState);
- Bundle args = getArguments();
- boolean allowContextualActions = (args != null) && args.getBoolean(ARG_ALLOW_CONTEXTUAL_MODE, false);
- if (allowContextualActions) {
- setChoiceModeAsMultipleModal(savedInstanceState);
- }
-
- Timber.i("onCreateView() end");
- return v;
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- mSortOptionsView = view.findViewById(R.id.options_layout);
- if (mSortOptionsView != null) {
- mSortOptionsView.setOnSortOptionsListener(this);
- if (isPickingAFolder()) {
- mSortOptionsView.setOnCreateFolderListener(this);
- mSortOptionsView.selectAdditionalView(SortOptionsView.AdditionalView.CREATE_FOLDER);
- }
- }
- }
-
- @Override
- public void onDetach() {
- setOnRefreshListener(null);
- mContainerActivity = null;
- super.onDetach();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- Timber.v("onActivityCreated() start");
-
- if (savedInstanceState != null) {
- mFile = savedInstanceState.getParcelable(KEY_FILE);
- mFileListOption = savedInstanceState.getParcelable(KEY_FILE_LIST_OPTION);
- }
-
- if (mFileListOption == null && getArguments() != null && getArguments().getParcelable(ARG_LIST_FILE_OPTION) != null) {
- mFileListOption = getArguments().getParcelable(ARG_LIST_FILE_OPTION);
- }
-
- if (mFileListOption == null) {
- mFileListOption = FileListOption.ALL_FILES;
- }
- updateListOfFiles(mFileListOption);
- }
-
- @Override
- public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
- super.onCreateOptionsMenu(menu, inflater);
- mSearchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
- mSearchView.setMaxWidth(Integer.MAX_VALUE);
- mSearchView.setQueryHint(getResources().getString(R.string.actionbar_search));
- mSearchView.setOnQueryTextFocusChangeListener(this);
- mSearchView.setOnQueryTextListener(this);
-
- if (isPickingAFolder()) {
- menu.removeItem(menu.findItem(R.id.action_share_current_folder).getItemId());
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (item.getItemId() == R.id.action_share_current_folder) {
- mContainerActivity.getFileOperationsHelper().showShareFile(mFile);
- }
- return super.onOptionsItemSelected(item);
- }
-
- public void updateFileListOption(FileListOption newFileListOption) {
- updateListOfFiles(newFileListOption);
- mFileListOption = newFileListOption;
- mFile = mContainerActivity.getStorageManager().getFileByPath(OCFile.ROOT_PATH);
- listDirectory(true);
- }
-
- private void updateListOfFiles(
- FileListOption fileListOption
- ) {
- boolean justFolders = isShowingJustFolders();
- setFooterEnabled(!justFolders);
-
- boolean folderPicker = isPickingAFolder();
-
- mFileListAdapter = new FileListListAdapter(
- justFolders,
- fileListOption.isAvailableOffline(),
- fileListOption.isSharedByLink(),
- folderPicker,
- getActivity(),
- mContainerActivity
- );
- setListAdapter(mFileListAdapter);
-
- mHideFab = !fileListOption.isAllFiles() || folderPicker;
- if (mHideFab) {
- setFabEnabled(false);
- } else {
- setFabEnabled(true);
- registerFabListeners();
-
- // detect if a mini FAB has ever been clicked
- final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
- if (prefs.getLong(KEY_FAB_EVER_CLICKED, 0) > 0) {
- miniFabClicked = true;
- }
-
- // add labels to the min FABs when none of them has ever been clicked on
- if (!miniFabClicked) {
- setFabLabels();
- } else {
- removeFabLabels();
- }
- }
-
- // Allow or disallow touches with other visible windows
- CoordinatorLayout coordinatorLayout = requireActivity().findViewById(R.id.coordinator_layout);
- coordinatorLayout.setFilterTouchesWhenObscured(
- PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(getContext())
- );
- }
-
- /**
- * adds labels to all mini FABs.
- */
- private void setFabLabels() {
- getFabUpload().setTitle(getResources().getString(R.string.actionbar_upload));
- getFabMkdir().setTitle(getResources().getString(R.string.actionbar_mkdir));
- }
-
- /**
- * registers all listeners on all mini FABs.
- */
- private void registerFabListeners() {
- registerFabUploadListeners();
- registerFabMkDirListeners();
- }
-
- /**
- * registers {@link android.view.View.OnClickListener} and {@link android.view.View.OnLongClickListener}
- * on the Upload mini FAB for the linked action an {@link Snackbar} showing the underlying action.
- */
- private void registerFabUploadListeners() {
- getFabUpload().setOnClickListener(v -> {
- final View uploadBottomSheet = getLayoutInflater().inflate(R.layout.upload_bottom_sheet_fragment, null);
- final BottomSheetDialog dialog = new BottomSheetDialog(requireContext());
- dialog.setContentView(uploadBottomSheet);
- final BottomSheetFragmentItemView uploadFromFilesItemView = uploadBottomSheet.findViewById(R.id.upload_from_files_item_view);
- BottomSheetFragmentItemView uploadFromCameraItemView =
- uploadBottomSheet.findViewById(R.id.upload_from_camera_item_view);
- TextView uploadToTextView = uploadBottomSheet.findViewById(R.id.upload_to_text_view);
- uploadFromFilesItemView.setOnTouchListener((v13, event) -> {
- Intent action = new Intent(Intent.ACTION_GET_CONTENT);
- action = action.setType(ALL_FILES_SAF_REGEX).addCategory(Intent.CATEGORY_OPENABLE);
- action.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
- getActivity().startActivityForResult(
- Intent.createChooser(action, getString(R.string.upload_chooser_title)),
- FileDisplayActivity.REQUEST_CODE__SELECT_CONTENT_FROM_APPS
- );
- dialog.hide();
- return false;
- });
- uploadFromCameraItemView.setOnTouchListener((v12, event) -> {
- ((FileDisplayActivity) getActivity()).getFilesUploadHelper().uploadFromCamera(FileDisplayActivity.REQUEST_CODE__UPLOAD_FROM_CAMERA);
- dialog.hide();
- return false;
- });
- uploadToTextView.setText(String.format(getResources().getString(R.string.upload_to),
- getResources().getString(R.string.app_name)));
- final BottomSheetBehavior uploadBottomSheetBehavior =
- BottomSheetBehavior.from((View) uploadBottomSheet.getParent());
- dialog.setOnShowListener(dialog1 ->
- uploadBottomSheetBehavior.setPeekHeight(uploadBottomSheet.getMeasuredHeight()));
- dialog.show();
- DialogExtKt.avoidScreenshotsIfNeeded(dialog);
- getFabMain().collapse();
- recordMiniFabClick();
- });
-
- getFabUpload().setOnLongClickListener(v -> {
- showSnackMessage(R.string.actionbar_upload);
- return true;
- });
- }
-
- /**
- * Registers {@link android.view.View.OnClickListener} and {@link android.view.View.OnLongClickListener}
- * on the 'Create Dir' mini FAB for the linked action and {@link Snackbar} showing the underlying action.
- */
- private void registerFabMkDirListeners() {
- getFabMkdir().setOnClickListener(v -> {
- CreateFolderDialogFragment dialog = CreateFolderDialogFragment.newInstance(mFile, this);
- dialog.show(requireActivity().getSupportFragmentManager(), DIALOG_CREATE_FOLDER);
- getFabMain().collapse();
- recordMiniFabClick();
- });
-
- getFabMkdir().setOnLongClickListener(v -> {
- showSnackMessage(R.string.actionbar_mkdir);
- return true;
- });
- }
-
- /**
- * records a click on a mini FAB and thus:
- *
- * - persists the click fact
- * - removes the mini FAB labels
- *
- */
- private void recordMiniFabClick() {
- // only record if it hasn't been done already at some other time
- if (!miniFabClicked) {
- final SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
- sp.edit().putLong(KEY_FAB_EVER_CLICKED, 1).apply();
- miniFabClicked = true;
- }
- }
-
- /**
- * removes the labels on all known min FABs.
- */
- private void removeFabLabels() {
- getFabUpload().setTitle(null);
- getFabMkdir().setTitle(null);
- ((TextView) getFabUpload().getTag(com.getbase.floatingactionbutton.R.id.fab_label)).setVisibility(View.GONE);
- ((TextView) getFabMkdir().getTag(com.getbase.floatingactionbutton.R.id.fab_label)).setVisibility(View.GONE);
- }
-
- @Override
- public boolean onQueryTextSubmit(String query) {
- return false;
- }
-
- @Override
- public boolean onQueryTextChange(String query) {
- mFileListAdapter.filterBySearch(query);
- return true;
- }
-
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- if (hasFocus) {
- setMessageForEmptyList(getString(R.string.local_file_list_search_with_no_matches));
- } else { // Set default message for empty list of files
-// if (requireActivity() instanceof FileDisplayActivity) {
-// ((FileDisplayActivity) requireActivity()).setBackgroundText();
-// } else if (requireActivity() instanceof FolderPickerActivity) {
-// ((FolderPickerActivity) requireActivity()).setBackgroundText();
-// }
- }
- }
-
- public boolean isSingleItemChecked() {
- return mFileListAdapter.getCheckedItems(getListView()).size() == 1;
- }
-
- @Override
- public void onViewTypeListener(@NotNull ViewType viewType) {
- mSortOptionsView.setViewTypeSelected(viewType);
- if (viewType == ViewType.VIEW_TYPE_LIST) {
- setListAsPreferred();
- } else {
- setGridAsPreferred();
- }
- }
-
- @Override
- public void onSortSelected(@NotNull SortType sortType) {
- mSortOptionsView.setSortTypeSelected(sortType);
-
- boolean isAscending = mSortOptionsView.getSortOrderSelected().equals(SortOrder.SORT_ORDER_ASCENDING);
- if (sortType == SortType.SORT_TYPE_BY_NAME) {
- sortByName(isAscending);
- } else if (sortType == SortType.SORT_TYPE_BY_DATE) {
- sortByDate(isAscending);
- } else if (sortType == SortType.SORT_TYPE_BY_SIZE) {
- sortBySize(isAscending);
- }
-
- }
-
- @Override
- public void onSortTypeListener(@NotNull SortType sortType, @NotNull SortOrder sortOrder) {
- SortBottomSheetFragment sortBottomSheetFragment = SortBottomSheetFragment.Companion.newInstance(sortType, sortOrder);
- sortBottomSheetFragment.setSortDialogListener(this);
- sortBottomSheetFragment.show(getChildFragmentManager(), SortBottomSheetFragment.TAG);
- }
-
- @Override
- public void onCreateFolderListener() {
- CreateFolderDialogFragment dialog = CreateFolderDialogFragment.newInstance(mFile, this::onFolderNameSet);
- dialog.show(requireActivity().getSupportFragmentManager(), DIALOG_CREATE_FOLDER);
-
- }
-
- @Override
- public void onFolderNameSet(@NotNull String newFolderName, @NotNull OCFile parentFolder) {
- FilesViewModel filesViewModel = get(FilesViewModel.class);
-
- filesViewModel.createFolder(parentFolder, newFolderName);
- filesViewModel.getCreateFolder().observe(this, uiResultEvent -> {
- UIResult uiResult = uiResultEvent.peekContent();
- if (uiResult.isSuccess()) {
- listDirectory(true);
- } else {
- Throwable throwable = uiResult.getThrowableOrNull();
- CharSequence errorMessage = ThrowableExtKt.parseError(throwable, getResources().getString(R.string.create_dir_fail_msg),
- getResources(), false);
- showSnackMessage(errorMessage.toString());
- }
-
- });
- }
-
- /**
- * Handler for multiple selection mode.
- *
- * Manages input from the user when one or more files or folders are selected in the list.
- *
- * Also listens to changes in navigation drawer to hide and recover multiple selection when it's opened
- * and closed.
- */
- private class MultiChoiceModeListener
- implements AbsListView.MultiChoiceModeListener, DrawerLayout.DrawerListener {
-
- private static final String KEY_ACTION_MODE_CLOSED_BY_DRAWER = "KILLED_ACTION_MODE";
- private static final String KEY_SELECTION_WHEN_CLOSED_BY_DRAWER = "CHECKED_ITEMS";
-
- /**
- * True when action mode is finished because the drawer was opened
- */
- private boolean mActionModeClosedByDrawer = false;
-
- /**
- * Selected items in list when action mode is closed by drawer
- */
- private SparseBooleanArray mSelectionWhenActionModeClosedByDrawer = null;
-
- @Override
- public void onDrawerSlide(@NonNull View drawerView, float slideOffset) {
- // nothing to do
- }
-
- @Override
- public void onDrawerOpened(@NonNull View drawerView) {
- clearLocalSearchView();
- }
-
- /**
- * When the navigation drawer is closed, action mode is recovered in the same state as was
- * when the drawer was (started to be) opened.
- *
- * @param drawerView Navigation drawer just closed.
- */
- @Override
- public void onDrawerClosed(@NonNull View drawerView) {
- if (mSelectionWhenActionModeClosedByDrawer != null && mActionModeClosedByDrawer) {
- for (int i = 0; i < mSelectionWhenActionModeClosedByDrawer.size(); i++) {
- if (mSelectionWhenActionModeClosedByDrawer.valueAt(i)) {
- getListView().setItemChecked(
- mSelectionWhenActionModeClosedByDrawer.keyAt(i),
- true
- );
- }
- }
- }
- mSelectionWhenActionModeClosedByDrawer = null;
- }
-
- /**
- * If the action mode is active when the navigation drawer starts to move, the action
- * mode is closed and the selection stored to be recovered when the drawer is closed.
- *
- * @param newState One of STATE_IDLE, STATE_DRAGGING or STATE_SETTLING.
- */
- @Override
- public void onDrawerStateChanged(int newState) {
- if (DrawerLayout.STATE_DRAGGING == newState && mActiveActionMode != null) {
- mSelectionWhenActionModeClosedByDrawer = getListView().getCheckedItemPositions().clone();
- mActiveActionMode.finish();
- mActionModeClosedByDrawer = true;
- }
- }
-
- /**
- * Update action mode bar when an item is selected / unselected in the list
- */
- @Override
- public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
- getListView().invalidateViews();
- mode.invalidate();
- if (mFileListAdapter.getCheckedItems(getListView()).size() == mFileListAdapter.getCount()) {
- mEnableSelectAll = false;
- } else {
- if (!checked) {
- mEnableSelectAll = true;
- }
- }
- }
-
- /**
- * Load menu and customize UI when action mode is started.
- */
- @Override
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- mActiveActionMode = mode;
-
- MenuInflater inflater = requireActivity().getMenuInflater();
- inflater.inflate(R.menu.file_actions_menu, menu);
- mode.invalidate();
-
- //set gray color
- Window w = getActivity().getWindow();
- mStatusBarColor = w.getStatusBarColor();
- w.setStatusBarColor(mStatusBarColorActionMode);
-
- // hide FAB in multi selection mode
- setFabEnabled(false);
- ((FileDisplayActivity) mContainerActivity).showBottomNavBar(false);
-
- // Hide sort options view in multi-selection mode
- mSortOptionsView.setVisibility(View.GONE);
-
- return true;
- }
-
- /**
- * Updates available action in menu depending on current selection.
- */
- @Override
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- List checkedFiles = mFileListAdapter.getCheckedItems(getListView());
- final int checkedCount = checkedFiles.size();
- String title = getResources().getQuantityString(
- R.plurals.items_selected_count,
- checkedCount,
- checkedCount
- );
- mode.setTitle(title);
- FileMenuFilter mf = new FileMenuFilter(
- checkedFiles,
- ((FileActivity) requireActivity()).getAccount(),
- mContainerActivity,
- getActivity()
- );
- mf.filter(menu, mEnableSelectAll, true, mFileListOption.isAvailableOffline(),
- mFileListOption.isSharedByLink());
- return true;
- }
-
- /**
- * Starts the corresponding action when a menu item is tapped by the user.
- */
- @Override
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- return onFileActionChosen(item.getItemId());
- }
-
- /**
- * Restores UI.
- */
- @Override
- public void onDestroyActionMode(ActionMode mode) {
- mActiveActionMode = null;
-
- // reset to previous color
- requireActivity().getWindow().setStatusBarColor(mStatusBarColor);
-
- // show FAB on multi selection mode exit
- if (!mHideFab) {
- setFabEnabled(true);
- }
- ((FileDisplayActivity) mContainerActivity).showBottomNavBar(true);
-
- // Show sort options view when multi-selection mode finish
- mSortOptionsView.setVisibility(View.VISIBLE);
- }
-
- void storeStateIn(Bundle outState) {
- outState.putBoolean(KEY_ACTION_MODE_CLOSED_BY_DRAWER, mActionModeClosedByDrawer);
- if (mSelectionWhenActionModeClosedByDrawer != null) {
- SparseBooleanArrayParcelable sbap = new SparseBooleanArrayParcelable(
- mSelectionWhenActionModeClosedByDrawer
- );
- outState.putParcelable(KEY_SELECTION_WHEN_CLOSED_BY_DRAWER, sbap);
- }
- }
-
- void loadStateFrom(Bundle savedInstanceState) {
- mActionModeClosedByDrawer = savedInstanceState.getBoolean(
- KEY_ACTION_MODE_CLOSED_BY_DRAWER,
- mActionModeClosedByDrawer
- );
- SparseBooleanArrayParcelable sbap = savedInstanceState.getParcelable(
- KEY_SELECTION_WHEN_CLOSED_BY_DRAWER
- );
- if (sbap != null) {
- mSelectionWhenActionModeClosedByDrawer = sbap.getSparseBooleanArray();
- }
- }
- }
-
- private void clearLocalSearchView() {
- FragmentExtKt.hideSoftKeyboard(this);
- mFileListAdapter.clearFilterBySearch();
- if (mSearchView != null) {
- mSearchView.onActionViewCollapsed();
- }
- }
-
- /**
- * Init listener that will handle interactions in multiple selection mode.
- */
- private void setChoiceModeAsMultipleModal(Bundle savedInstanceState) {
- setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
- mMultiChoiceModeListener = new MultiChoiceModeListener();
- if (savedInstanceState != null) {
- mMultiChoiceModeListener.loadStateFrom(savedInstanceState);
- }
- setMultiChoiceModeListener(mMultiChoiceModeListener);
- ((FileActivity) requireActivity()).addDrawerListener(mMultiChoiceModeListener);
- }
-
- /**
- * Saves the current listed folder
- */
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putParcelable(KEY_FILE, mFile);
- outState.putParcelable(KEY_FILE_LIST_OPTION, mFileListOption);
-
- // If this fragment is used to show target folders where a selected file/folder can be
- // copied/moved, multiple choice is disabled
- if (mMultiChoiceModeListener != null) {
- mMultiChoiceModeListener.storeStateIn(outState);
- }
- }
-
- /**
- * Call this, when the user presses the up button.
- *
- * Tries to move up the current folder one level. If the parent folder was removed from the
- * database, it continues browsing up until finding an existing folders.
- *
- * return Count of folder levels browsed up.
- */
- public int onBrowseUp() {
- OCFile parentDir;
- int moveCount = 0;
-
- if (mFile != null) {
- FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
-
- String parentPath = null;
- if (mFile.getParentId() != FileDataStorageManager.ROOT_PARENT_ID) {
- parentPath = new File(mFile.getRemotePath()).getParent();
- parentPath = parentPath.endsWith(File.separator) ? parentPath :
- parentPath + File.separator;
- parentDir = storageManager.getFileByPath(parentPath);
- moveCount++;
- } else {
- parentDir = storageManager.getFileByPath(OCFile.ROOT_PATH);
- }
- while (parentDir == null) {
- parentPath = new File(parentPath).getParent();
- parentPath = parentPath.endsWith(File.separator) ? parentPath :
- parentPath + File.separator;
- parentDir = storageManager.getFileByPath(parentPath);
- moveCount++;
- } // exit is granted because storageManager.getFileByPath("/") never returns null
-
- // FIXME: 13/10/2020 : New_arch: Av.Offline
-// if (mFileListOption.isAvailableOffline() && !parentDir.isAvailableOffline()) {
-// parentDir = storageManager.getFileByPath(OCFile.ROOT_PATH);
-// }
-
- if (mFileListOption.isSharedByLink() && !parentDir.getSharedByLink()) {
- parentDir = storageManager.getFileByPath(OCFile.ROOT_PATH);
- }
-
- mFile = parentDir;
-
- listDirectoryWidthAnimationUp(mFile);
-
- onRefresh(false);
-
- // restore index and top position
- restoreIndexAndTopPosition();
-
- } // else - should never happen now
-
- return moveCount;
- }
-
- private void listDirectoryWithAnimationDown(final OCFile file) {
- if (isInPowerSaveMode()) {
- listDirectory(file);
- } else {
- Animation fadeOutFront = AnimationUtils.loadAnimation(getContext(), R.anim.dir_fadeout_front);
- Handler eventHandler = new Handler();
-
- // This is a ugly hack for getting rid of the "ArrayOutOfBound" exception we get when we
- // call listDirectory() from the Animation callback
- eventHandler.postDelayed(() -> {
- listDirectory(file);
- Animation fadeInBack = AnimationUtils.loadAnimation(getContext(), R.anim.dir_fadein_back);
- getListView().setAnimation(fadeInBack);
- }, getResources().getInteger(R.integer.folder_animation_duration));
- getListView().startAnimation(fadeOutFront);
- }
- }
-
- private boolean isInPowerSaveMode() {
- PowerManager powerManager = (PowerManager) requireActivity().getSystemService(Context.POWER_SERVICE);
- return (powerManager != null) && powerManager.isPowerSaveMode();
- }
-
- private void listDirectoryWidthAnimationUp(final OCFile file) {
- if (isInPowerSaveMode()) {
- listDirectory(file);
- } else {
- if (getListView().getVisibility() == View.GONE) {
- listDirectory(file);
- Animation fadeInFront = AnimationUtils.loadAnimation(getContext(), R.anim.dir_fadein_front);
- getListView().startAnimation(fadeInFront);
- return;
- }
-
- Handler eventHandler = new Handler();
- Animation fadeOutBack = AnimationUtils.loadAnimation(getContext(), R.anim.dir_fadeout_back);
-
- // This is a ugly hack for getting rid of the "ArrayOutOfBound" exception we get when we
- // call listDirectory() from the Animation callback
- eventHandler.postDelayed(() -> {
- listDirectory(file);
- Animation fadeInFront = AnimationUtils.loadAnimation(getContext(), R.anim.dir_fadein_front);
- getListView().startAnimation(fadeInFront);
- }, getResources().getInteger(R.integer.folder_animation_duration));
- getListView().startAnimation(fadeOutBack);
- }
- }
-
- @Override
- public void onItemClick(AdapterView> l, View v, int position, long id) {
- OCFile file = (OCFile) mFileListAdapter.getItem(position);
- if (file != null) {
- if (file.isFolder()) {
- listDirectoryWithAnimationDown(file);
- // then, notify parent activity to let it update its state and view
- mContainerActivity.onBrowsedDownTo(file);
- // save index and top position
- saveIndexAndTopPosition(position);
- } else { /// Click on a file
- if (PreviewImageFragment.canBePreviewed(file)) {
- // preview image - it handles the sync, if needed
- ((FileDisplayActivity) mContainerActivity).startImagePreview(file);
- } else if (PreviewTextFragment.canBePreviewed(file)) {
- ((FileDisplayActivity) mContainerActivity).startTextPreview(file);
- mContainerActivity.getFileOperationsHelper().syncFile(file);
-
- } else if (PreviewAudioFragment.canBePreviewed(file)) {
- // media preview
- ((FileDisplayActivity) mContainerActivity).startAudioPreview(file, 0);
- mContainerActivity.getFileOperationsHelper().syncFile(file);
-
- } else if (PreviewVideoFragment.canBePreviewed(file) &&
- !fileIsDownloading(file)) {
- // FIXME: 13/10/2020 : New_arch: Av.Offline
- // Available offline exception, don't initialize streaming
- // if (!file.isAvailableLocally() && file.isAvailableOffline()) {
- if (file.isAvailableLocally()) {
- // sync file content, then open with external apps
- ((FileDisplayActivity) mContainerActivity).startSyncThenOpen(file);
- } else {
- // media preview
- ((FileDisplayActivity) mContainerActivity).startVideoPreview(file, 0);
- }
-
- // If the file is already downloaded sync it, just to update it if there is a
- // new available file version
- if (file.isAvailableLocally()) {
- mContainerActivity.getFileOperationsHelper().syncFile(file);
- }
- } else {
- // sync file content, then open with external apps
- ((FileDisplayActivity) mContainerActivity).startSyncThenOpen(file);
- }
-
- }
-
- } else {
- Timber.d("Null object in ListAdapter!!");
- }
-
- }
-
- /**
- * @return 'true' if the file is being downloaded, 'false' otherwise.
- */
- private boolean fileIsDownloading(OCFile file) {
- return WorkManagerExtKt.isDownloadPending(
- WorkManager.getInstance(getContext()),
- ((FileActivity) mContainerActivity).getAccount(),
- file
- );
- }
-
- public void selectAll() {
- for (int i = 0; i < mFileListAdapter.getCount(); i++) {
- getListView().setItemChecked(i, true);
- }
- }
-
- public int getNoOfItems() {
- return getListView().getCount();
- }
-
- /**
- * Start the appropriate action(s) on the currently selected files given menu selected by the user.
- *
- * @param menuId Identifier of the action menu selected by the user
- * @return 'true' if the menu selection started any action, 'false' otherwise.
- */
- private boolean onFileActionChosen(int menuId) {
- final ArrayList checkedFiles = mFileListAdapter.getCheckedItems(getListView());
- if (checkedFiles.size() <= 0) {
- return false;
- }
-
- if (checkedFiles.size() == 1) {
- /// action only possible on a single file
- OCFile singleFile = checkedFiles.get(0);
- switch (menuId) {
- case R.id.action_share_file: {
- mContainerActivity.getFileOperationsHelper().showShareFile(singleFile);
- mEnableSelectAll = false;
- return true;
- }
- case R.id.action_open_file_with: {
- if (!singleFile.isAvailableLocally()) { // Download the file
- ((FileDisplayActivity) mContainerActivity).startDownloadForOpening(singleFile);
- } else {
- mContainerActivity.getFileOperationsHelper().openFile(singleFile);
- }
- return true;
- }
- case R.id.action_rename_file: {
- RenameFileDialogFragment dialog = RenameFileDialogFragment.newInstance(singleFile);
- dialog.show(getFragmentManager(), FileDetailFragment.FTAG_RENAME_FILE);
- return true;
- }
- case R.id.action_see_details: {
- if (mActiveActionMode != null) {
- mActiveActionMode.finish();
- }
- mContainerActivity.showDetails(singleFile);
- return true;
- }
- case R.id.action_send_file: {
- // Obtain the file
- if (!singleFile.isAvailableLocally()) { // Download the file
- Timber.d("%s : File must be downloaded", singleFile.getRemotePath());
- ((FileDisplayActivity) mContainerActivity).startDownloadForSending(singleFile);
- } else {
- mContainerActivity.getFileOperationsHelper().sendDownloadedFile(singleFile);
- }
- return true;
- }
- }
- }
-
- /// actions possible on a batch of files
- switch (menuId) {
- case R.id.file_action_select_all: {
- selectAll();
- return true;
- }
- case R.id.action_select_inverse: {
- for (int i = 0; i < mFileListAdapter.getCount(); i++) {
- if (getListView().isItemChecked(i)) {
- getListView().setItemChecked(i, false);
- } else {
- getListView().setItemChecked(i, true);
- }
- }
- return true;
- }
- case R.id.action_send_file: {
- if (checkedFiles.size() > 1 && filesAreDown(checkedFiles)) {
- mContainerActivity.getFileOperationsHelper().sendDownloadedFiles(checkedFiles);
- }
- return true;
- }
- case R.id.action_remove_file: {
- RemoveFilesDialogFragment dialog = RemoveFilesDialogFragment.newInstance(checkedFiles);
- dialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION);
- return true;
- }
- case R.id.action_download_file:
- case R.id.action_sync_file: {
- mContainerActivity.getFileOperationsHelper().syncFiles(checkedFiles);
- return true;
- }
- case R.id.action_cancel_sync: {
- ((FileDisplayActivity) mContainerActivity).cancelTransference(checkedFiles);
- return true;
- }
- case R.id.action_set_available_offline: {
- mContainerActivity.getFileOperationsHelper().toggleAvailableOffline(checkedFiles, true);
- getListView().invalidateViews();
- return true;
- }
- case R.id.action_unset_available_offline: {
- mContainerActivity.getFileOperationsHelper().toggleAvailableOffline(checkedFiles, false);
- getListView().invalidateViews();
- invalidateActionMode();
- if (mFileListOption.isAvailableOffline()) {
- onRefresh();
- }
- return true;
- }
- case R.id.action_move: {
- Intent action = new Intent(getActivity(), FolderPickerActivity.class);
- action.putExtra(FolderPickerActivity.EXTRA_PICKER_OPTION, FolderPickerActivity.PickerMode.MOVE);
- action.putParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES, checkedFiles);
- requireActivity().startActivityForResult(action, FileDisplayActivity.REQUEST_CODE__MOVE_FILES);
- return true;
- }
- case R.id.action_copy:
- Intent action = new Intent(getActivity(), FolderPickerActivity.class);
- action.putExtra(FolderPickerActivity.EXTRA_PICKER_OPTION, FolderPickerActivity.PickerMode.COPY);
- action.putParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES, checkedFiles);
- requireActivity().startActivityForResult(action, FileDisplayActivity.REQUEST_CODE__COPY_FILES);
- return true;
- default:
- return false;
- }
- }
-
- /**
- * Use this to query the {@link OCFile} that is currently
- * being displayed by this fragment
- *
- * @return The currently viewed OCFile
- */
- public OCFile getCurrentFile() {
- return mFile;
- }
-
- /**
- * Calls {@link OCFileListFragment#listDirectory(OCFile)} with a null parameter
- */
- public void listDirectory(boolean reloadData) {
- if (reloadData) {
- listDirectory(null);
- } else {
- getListView().invalidateViews();
- }
- }
-
- /**
- * Lists the given directory on the view. When the input parameter is null,
- * it will either refresh the last known directory. list the root
- * if there never was a directory.
- *
- * @param directory File to be listed
- */
- public void listDirectory(OCFile directory) {
- if (mContainerActivity == null) {
- Timber.e("No container activity attached");
- return;
- }
-
- FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
- if (storageManager != null) {
-
- // Check input parameters for null
- if (directory == null) {
- if (mFile != null) {
- directory = mFile;
- } else {
- directory = storageManager.getFileByPath(OCFile.ROOT_PATH);
- if (directory == null) {
- return; // no files, wait for sync
- }
- }
- }
-
- // If that's not a directory -> List its parent
- if (!directory.isFolder()) {
- Timber.w("You see, that is not a directory -> %s", directory.toString());
- directory = storageManager.getFileById(directory.getParentId());
- }
-
- mFileListAdapter.swapDirectory(directory, storageManager);
- if (mFile == null || !mFile.equals(directory)) {
- mCurrentListView.setSelection(0);
- }
- mFile = directory;
-
- updateLayout();
- }
- }
-
- private boolean filesAreDown(ArrayList checkedFiles) {
- for (int i = 0; i < checkedFiles.size(); i++) {
- if (!checkedFiles.get(i).isAvailableLocally()) {
- Timber.d("%s : File must be downloaded", checkedFiles.get(i).getRemotePath());
- return false;
- }
- }
- return true;
- }
-
- private void updateLayout() {
- if (!isShowingJustFolders()) {
- int filesCount = 0, foldersCount = 0;
- int count = mFileListAdapter.getCount();
- OCFile file;
- for (int i = 0; i < count; i++) {
- file = (OCFile) mFileListAdapter.getItem(i);
- if (file.isFolder()) {
- foldersCount++;
- } else {
- if (!file.isHidden()) {
- filesCount++;
- }
- }
- }
-
- if (count == 0) {
- int emptyMessage;
- if (mFileListOption == FileListOption.AV_OFFLINE) {
- emptyMessage = R.string.file_list_empty_available_offline;
- } else if (mFileListOption == FileListOption.SHARED_BY_LINK) {
- emptyMessage = R.string.file_list_empty_shared_by_links;
- } else {
- emptyMessage = R.string.file_list_empty;
- }
-
- setMessageForEmptyList(getString(emptyMessage));
- }
-
- // decide grid vs list view
- OwnCloudVersion version = AccountUtils.getServerVersion(((FileActivity) mContainerActivity).getAccount());
- if (version != null && isGridViewPreferred(mFile)) {
- switchToGridView();
- mSortOptionsView.setViewTypeSelected(ViewType.VIEW_TYPE_GRID);
- } else {
- switchToListView();
- mSortOptionsView.setViewTypeSelected(ViewType.VIEW_TYPE_LIST);
- }
-
- // set footer text
- setFooterText(generateFooterText(filesCount, foldersCount));
- }
- invalidateActionMode();
- clearLocalSearchView();
- }
-
- private void invalidateActionMode() {
- if (mActiveActionMode != null) {
- mActiveActionMode.invalidate();
- }
- }
-
- private String generateFooterText(int filesCount, int foldersCount) {
- String output;
- if (filesCount <= 0) {
- if (foldersCount <= 0) {
- output = "";
-
- } else if (foldersCount == 1) {
- output = getResources().getString(R.string.file_list__footer__folder);
-
- } else { // foldersCount > 1
- output = getResources().getString(R.string.file_list__footer__folders, foldersCount);
- }
-
- } else if (filesCount == 1) {
- if (foldersCount <= 0) {
- output = getResources().getString(R.string.file_list__footer__file);
-
- } else if (foldersCount == 1) {
- output = getResources().getString(R.string.file_list__footer__file_and_folder);
-
- } else { // foldersCount > 1
- output = getResources().getString(R.string.file_list__footer__file_and_folders, foldersCount);
- }
- } else { // filesCount > 1
- if (foldersCount <= 0) {
- output = getResources().getString(R.string.file_list__footer__files, filesCount);
-
- } else if (foldersCount == 1) {
- output = getResources().getString(R.string.file_list__footer__files_and_folder, filesCount);
-
- } else { // foldersCount > 1
- output = getResources().getString(
- R.string.file_list__footer__files_and_folders, filesCount, foldersCount
- );
-
- }
- }
- return output;
- }
-
- private void sortByName(boolean descending) {
- mFileListAdapter.setSortOrder(FileStorageUtils.SORT_NAME, descending);
- }
-
- private void sortByDate(boolean descending) {
- mFileListAdapter.setSortOrder(FileStorageUtils.SORT_DATE, descending);
- }
-
- private void sortBySize(boolean descending) {
- mFileListAdapter.setSortOrder(FileStorageUtils.SORT_SIZE, descending);
- }
-
- /**
- * Determines if user set folder to grid or list view. If folder is not set itself,
- * it finds a parent that is set (at least root is set).
- *
- * @param file Folder to check.
- * @return 'true' is folder should be shown in grid mode, 'false' if list mode is preferred.
- */
- private boolean isGridViewPreferred(OCFile file) {
- if (file != null) {
- OCFile fileToTest = file;
- OCFile parentDir;
- String parentPath = null;
- FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
-
- SharedPreferences setting =
- requireActivity().getSharedPreferences(GRID_IS_PREFERED_PREFERENCE, Context.MODE_PRIVATE);
-
- if (setting.contains(String.valueOf(fileToTest.getId()))) {
- return setting.getBoolean(String.valueOf(fileToTest.getId()), false);
- } else {
- do {
- if (fileToTest.getParentId() != FileDataStorageManager.ROOT_PARENT_ID) {
- parentPath = new File(fileToTest.getRemotePath()).getParent();
- parentPath = parentPath.endsWith(File.separator) ? parentPath :
- parentPath + File.separator;
- parentDir = storageManager.getFileByPath(parentPath);
- } else {
- parentDir = storageManager.getFileByPath(OCFile.ROOT_PATH);
- }
-
- while (parentDir == null) {
- parentPath = new File(parentPath).getParent();
- parentPath = parentPath.endsWith(File.separator) ? parentPath :
- parentPath + File.separator;
- parentDir = storageManager.getFileByPath(parentPath);
- }
- fileToTest = parentDir;
- } while (endWhile(parentDir, setting));
- return setting.getBoolean(String.valueOf(fileToTest.getId()), false);
- }
- } else {
- return false;
- }
- }
-
- private boolean endWhile(OCFile parentDir, SharedPreferences setting) {
- if (parentDir.getRemotePath().compareToIgnoreCase(OCFile.ROOT_PATH) == 0) {
- return false;
- } else {
- return !setting.contains(String.valueOf(parentDir.getId()));
- }
- }
-
- public void setListAsPreferred() {
- saveGridAsPreferred(false);
- switchToListView();
- }
-
- public void setGridAsPreferred() {
- saveGridAsPreferred(true);
- switchToGridView();
- }
-
- private void saveGridAsPreferred(boolean setGrid) {
- SharedPreferences setting = requireActivity().getSharedPreferences(
- GRID_IS_PREFERED_PREFERENCE, Context.MODE_PRIVATE
- );
-
- SharedPreferences.Editor editor = setting.edit();
- editor.putBoolean(String.valueOf(mFile.getId()), setGrid);
- editor.apply();
- }
-
- /**
- * Show a temporary message in a Snackbar bound to the content view of the parent Activity
- *
- * @param messageResource Message to show.
- */
- private void showSnackMessage(int messageResource) {
- Snackbar snackbar = Snackbar.make(
- requireActivity().findViewById(R.id.coordinator_layout),
- messageResource,
- Snackbar.LENGTH_LONG
- );
- snackbar.show();
- }
-
- private void showSnackMessage(String messageResource) {
- Snackbar snackbar = Snackbar.make(
- requireActivity().findViewById(R.id.coordinator_layout),
- messageResource,
- Snackbar.LENGTH_LONG
- );
- snackbar.show();
- }
-
- public void setSearchListener(SearchView searchView){
- searchView.setOnQueryTextFocusChangeListener(this);
- searchView.setOnQueryTextListener(this);
- }
-}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/helpers/UriUploader.java b/owncloudApp/src/main/java/com/owncloud/android/ui/helpers/UriUploader.java
index 19901beead9..56a0978a64c 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/ui/helpers/UriUploader.java
+++ b/owncloudApp/src/main/java/com/owncloud/android/ui/helpers/UriUploader.java
@@ -101,7 +101,7 @@ public UriUploaderResultCode uploadUris() {
}
if (!contentUris.isEmpty()) {
- /// content: uris will be copied to temporary files before calling {@link FileUploader}
+ /// content: uris will be copied to temporary files before calling the upload usecase
copyThenUpload(contentUris.toArray(new Uri[0]), mUploadPath);
// Listen to CopyAndUploadContentUrisTask before killing the app or a SecurityException may appear.
diff --git a/owncloudApp/src/main/java/com/owncloud/android/usecases/UploadEnqueuedBy.kt b/owncloudApp/src/main/java/com/owncloud/android/usecases/UploadEnqueuedBy.kt
index 759010bed26..52e23124828 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/usecases/UploadEnqueuedBy.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/usecases/UploadEnqueuedBy.kt
@@ -18,7 +18,6 @@
*/
package com.owncloud.android.usecases
-import com.owncloud.android.operations.UploadFileOperation
import com.owncloud.android.workers.UploadFileFromContentUriWorker
@@ -35,15 +34,6 @@ enum class UploadEnqueuedBy {
ENQUEUED_AS_CAMERA_UPLOAD_PICTURE,
ENQUEUED_AS_CAMERA_UPLOAD_VIDEO;
- fun fromLegacyCreatedBy(oldCreatedBy: Int): UploadEnqueuedBy {
- return when (oldCreatedBy) {
- UploadFileOperation.CREATED_BY_USER -> ENQUEUED_BY_USER
- UploadFileOperation.CREATED_AS_CAMERA_UPLOAD_PICTURE -> ENQUEUED_AS_CAMERA_UPLOAD_PICTURE
- UploadFileOperation.CREATED_AS_CAMERA_UPLOAD_VIDEO -> ENQUEUED_AS_CAMERA_UPLOAD_VIDEO
- else -> ENQUEUED_BY_USER
- }
- }
-
fun toTransferTag(): String {
return when (this) {
ENQUEUED_BY_USER -> UploadFileFromContentUriWorker.TRANSFER_TAG_MANUAL_UPLOAD
diff --git a/owncloudApp/src/main/java/com/owncloud/android/utils/Extras.java b/owncloudApp/src/main/java/com/owncloud/android/utils/Extras.java
index abc11abbad5..ea3a1f024a4 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/utils/Extras.java
+++ b/owncloudApp/src/main/java/com/owncloud/android/utils/Extras.java
@@ -31,7 +31,6 @@ public class Extras {
// from FileDownloader
public static final String EXTRA_DOWNLOAD_RESULT = "RESULT";
- // from FileUploader
public static final String EXTRA_OLD_REMOTE_PATH = "OLD_REMOTE_PATH";
public static final String EXTRA_OLD_FILE_PATH = "OLD_FILE_PATH";
public static final String EXTRA_UPLOAD_RESULT = "RESULT";
diff --git a/owncloudApp/src/main/java/com/owncloud/android/workers/CameraUploadsWorker.kt b/owncloudApp/src/main/java/com/owncloud/android/workers/CameraUploadsWorker.kt
index 8b80f9e21d7..56e34700132 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/workers/CameraUploadsWorker.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/workers/CameraUploadsWorker.kt
@@ -35,9 +35,8 @@ import com.owncloud.android.domain.camerauploads.model.UploadBehavior
import com.owncloud.android.domain.camerauploads.usecases.GetCameraUploadsConfigurationUseCase
import com.owncloud.android.domain.camerauploads.usecases.SavePictureUploadsConfigurationUseCase
import com.owncloud.android.domain.camerauploads.usecases.SaveVideoUploadsConfigurationUseCase
-import com.owncloud.android.operations.UploadFileOperation.CREATED_AS_CAMERA_UPLOAD_PICTURE
-import com.owncloud.android.operations.UploadFileOperation.CREATED_AS_CAMERA_UPLOAD_VIDEO
import com.owncloud.android.presentation.ui.settings.SettingsActivity
+import com.owncloud.android.usecases.UploadEnqueuedBy
import com.owncloud.android.usecases.UploadFileFromContentUriUseCase
import com.owncloud.android.utils.MimetypeIconUtil
import com.owncloud.android.utils.NotificationUtils
@@ -143,8 +142,8 @@ class CameraUploadsWorker(
accountName = folderBackUpConfiguration.accountName,
behavior = folderBackUpConfiguration.behavior,
createdByWorker = when (syncType) {
- SyncType.PICTURE_UPLOADS -> CREATED_AS_CAMERA_UPLOAD_PICTURE
- SyncType.VIDEO_UPLOADS -> CREATED_AS_CAMERA_UPLOAD_VIDEO
+ SyncType.PICTURE_UPLOADS -> UploadEnqueuedBy.ENQUEUED_AS_CAMERA_UPLOAD_PICTURE.ordinal
+ SyncType.VIDEO_UPLOADS -> UploadEnqueuedBy.ENQUEUED_AS_CAMERA_UPLOAD_VIDEO.ordinal
}
)
enqueueSingleUpload(
diff --git a/owncloudApp/src/main/res/layout/files_folder_picker.xml b/owncloudApp/src/main/res/layout/files_folder_picker.xml
index 7e839c2fbbd..d94ebaaaa2f 100644
--- a/owncloudApp/src/main/res/layout/files_folder_picker.xml
+++ b/owncloudApp/src/main/res/layout/files_folder_picker.xml
@@ -49,7 +49,6 @@ along with this program. If not, see .
style="?android:buttonStyle"
android:layout_width="50dp"
android:layout_height="match_parent"
- android:layout_alignParentEnd="true"
android:src="@drawable/ic_home_black"
android:visibility="gone" />
diff --git a/owncloudApp/src/main/res/layout/grid_image.xml b/owncloudApp/src/main/res/layout/grid_image.xml
deleted file mode 100644
index 8ab4cd14985..00000000000
--- a/owncloudApp/src/main/res/layout/grid_image.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/owncloudApp/src/main/res/layout/list_fragment.xml b/owncloudApp/src/main/res/layout/list_fragment.xml
deleted file mode 100644
index a130d63d42e..00000000000
--- a/owncloudApp/src/main/res/layout/list_fragment.xml
+++ /dev/null
@@ -1,160 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/owncloudApp/src/main/res/layout/list_fragment_expandable.xml b/owncloudApp/src/main/res/layout/list_fragment_expandable.xml
index d2d112e4ffa..5c736d2ebd2 100755
--- a/owncloudApp/src/main/res/layout/list_fragment_expandable.xml
+++ b/owncloudApp/src/main/res/layout/list_fragment_expandable.xml
@@ -1,9 +1,4 @@
-
+ android:layout_height="32dp" />