android/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java

973 lines
42 KiB
Java
Executable File

/*
* Nextcloud Android client application
*
* @author Tobias Kaminsky
* @author Chris Narkiewicz <hello@ezaquarii.com>
*
* Copyright (C) 2018 Tobias Kaminsky
* Copyright (C) 2018 Nextcloud
* Copyright (C) 2019 Chris Narkiewicz <hello@ezaquarii.com>
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package com.owncloud.android.ui.adapter;
import android.app.NotificationManager;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.PopupMenu;
import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter;
import com.afollestad.sectionedrecyclerview.SectionedViewHolder;
import com.nextcloud.client.account.User;
import com.nextcloud.client.account.UserAccountManager;
import com.nextcloud.client.core.Clock;
import com.nextcloud.client.device.PowerManagementService;
import com.nextcloud.client.jobs.upload.FileUploadHelper;
import com.nextcloud.client.jobs.upload.FileUploadWorker;
import com.nextcloud.client.network.ConnectivityService;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.databinding.UploadListHeaderBinding;
import com.owncloud.android.databinding.UploadListItemBinding;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.datamodel.ThumbnailsCacheManager;
import com.owncloud.android.datamodel.UploadsStorageManager;
import com.owncloud.android.datamodel.UploadsStorageManager.UploadStatus;
import com.owncloud.android.db.OCUpload;
import com.owncloud.android.db.OCUploadComparator;
import com.owncloud.android.db.UploadResult;
import com.owncloud.android.lib.common.operations.OnRemoteOperationListener;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.operations.RefreshFolderOperation;
import com.owncloud.android.ui.activity.ConflictsResolveActivity;
import com.owncloud.android.ui.activity.FileActivity;
import com.owncloud.android.ui.activity.FileDisplayActivity;
import com.owncloud.android.ui.notifications.NotificationUtils;
import com.owncloud.android.ui.preview.PreviewImageFragment;
import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.MimeTypeUtil;
import com.owncloud.android.utils.theme.ViewThemeUtils;
import java.io.File;
import java.util.Arrays;
import java.util.Optional;
import androidx.annotation.NonNull;
/**
* This Adapter populates a ListView with following types of uploads: pending, active, completed. Filtering possible.
*/
public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedViewHolder> {
private static final String TAG = UploadListAdapter.class.getSimpleName();
private ProgressListener progressListener;
private final FileActivity parentActivity;
private final UploadsStorageManager uploadsStorageManager;
private final FileDataStorageManager storageManager;
private final ConnectivityService connectivityService;
private final PowerManagementService powerManagementService;
private final UserAccountManager accountManager;
private final Clock clock;
private final UploadGroup[] uploadGroups;
private final boolean showUser;
private final ViewThemeUtils viewThemeUtils;
private NotificationManager mNotificationManager;
private final FileUploadHelper uploadHelper = FileUploadHelper.Companion.instance();
@Override
public int getSectionCount() {
return uploadGroups.length;
}
@Override
public int getItemCount(int section) {
return uploadGroups[section].getItems().length;
}
@Override
public void onBindHeaderViewHolder(SectionedViewHolder holder, int section, boolean expanded) {
HeaderViewHolder headerViewHolder = (HeaderViewHolder) holder;
UploadGroup group = uploadGroups[section];
headerViewHolder.binding.uploadListTitle.setText(
String.format(parentActivity.getString(R.string.uploads_view_group_header),
group.getGroupName(), group.getGroupItemCount()));
viewThemeUtils.platform.colorPrimaryTextViewElement(headerViewHolder.binding.uploadListTitle);
headerViewHolder.binding.uploadListTitle.setOnClickListener(v -> toggleSectionExpanded(section));
switch (group.type) {
case CURRENT, FINISHED -> headerViewHolder.binding.uploadListAction.setImageResource(R.drawable.ic_close);
case CANCELLED, FAILED ->
headerViewHolder.binding.uploadListAction.setImageResource(R.drawable.ic_dots_vertical);
}
headerViewHolder.binding.uploadListAction.setOnClickListener(v -> {
switch (group.type) {
case CURRENT -> {
for (OCUpload upload : group.getItems()) {
uploadHelper.cancelFileUpload(upload.getRemotePath(), upload.getAccountName());
}
loadUploadItemsFromDb();
}
case FINISHED -> {
uploadsStorageManager.clearSuccessfulUploads();
loadUploadItemsFromDb();
}
case FAILED -> {
showFailedPopupMenu(headerViewHolder);
}
case CANCELLED -> {
showCancelledPopupMenu(headerViewHolder);
}
}
});
}
private void showFailedPopupMenu(HeaderViewHolder headerViewHolder) {
PopupMenu failedPopup = new PopupMenu(MainApp.getAppContext(), headerViewHolder.binding.uploadListAction);
failedPopup.inflate(R.menu.upload_list_failed_options);
failedPopup.setOnMenuItemClickListener(i -> {
int itemId = i.getItemId();
if (itemId == R.id.action_upload_list_failed_clear) {
uploadsStorageManager.clearFailedButNotDelayedUploads();
loadUploadItemsFromDb();
} else {
new Thread(() -> {
FileUploadHelper.Companion.instance().retryFailedUploads(
uploadsStorageManager,
connectivityService,
accountManager,
powerManagementService);
parentActivity.runOnUiThread(this::loadUploadItemsFromDb);
}).start();
}
return true;
});
failedPopup.show();
}
private void showCancelledPopupMenu(HeaderViewHolder headerViewHolder) {
PopupMenu popup = new PopupMenu(MainApp.getAppContext(), headerViewHolder.binding.uploadListAction);
popup.inflate(R.menu.upload_list_cancelled_options);
popup.setOnMenuItemClickListener(i -> {
int itemId = i.getItemId();
if (itemId == R.id.action_upload_list_cancelled_clear) {
uploadsStorageManager.clearCancelledUploadsForCurrentAccount();
loadUploadItemsFromDb();
} else if (itemId == R.id.action_upload_list_cancelled_resume) {
retryCancelledUploads();
}
return true;
});
popup.show();
}
private void retryCancelledUploads() {
new Thread(() -> {
boolean showNotExistMessage = FileUploadHelper.Companion.instance().retryCancelledUploads(
uploadsStorageManager,
connectivityService,
accountManager,
powerManagementService);
parentActivity.runOnUiThread(this::loadUploadItemsFromDb);
parentActivity.runOnUiThread(() -> {
if (showNotExistMessage) {
showNotExistMessage();
}
});
}).start();
}
private void showNotExistMessage() {
DisplayUtils.showSnackMessage(parentActivity, R.string.upload_action_file_not_exist_message);
}
@Override
public void onBindFooterViewHolder(SectionedViewHolder holder, int section) {
// not needed
}
public UploadListAdapter(final FileActivity fileActivity,
final UploadsStorageManager uploadsStorageManager,
final FileDataStorageManager storageManager,
final UserAccountManager accountManager,
final ConnectivityService connectivityService,
final PowerManagementService powerManagementService,
final Clock clock,
final ViewThemeUtils viewThemeUtils) {
Log_OC.d(TAG, "UploadListAdapter");
this.parentActivity = fileActivity;
this.uploadsStorageManager = uploadsStorageManager;
this.storageManager = storageManager;
this.accountManager = accountManager;
this.connectivityService = connectivityService;
this.powerManagementService = powerManagementService;
this.clock = clock;
this.viewThemeUtils = viewThemeUtils;
uploadGroups = new UploadGroup[4];
shouldShowHeadersForEmptySections(false);
uploadGroups[0] = new UploadGroup(Type.CURRENT,
parentActivity.getString(R.string.uploads_view_group_current_uploads)) {
@Override
public void refresh() {
fixAndSortItems(uploadsStorageManager.getCurrentAndPendingUploadsForCurrentAccount());
}
};
uploadGroups[1] = new UploadGroup(Type.FAILED,
parentActivity.getString(R.string.uploads_view_group_failed_uploads)) {
@Override
public void refresh() {
fixAndSortItems(uploadsStorageManager.getFailedButNotDelayedUploadsForCurrentAccount());
}
};
uploadGroups[2] = new UploadGroup(Type.CANCELLED,
parentActivity.getString(
R.string.uploads_view_group_manually_cancelled_uploads)) {
@Override
public void refresh() {
fixAndSortItems(uploadsStorageManager.getCancelledUploadsForCurrentAccount());
}
};
uploadGroups[3] = new UploadGroup(Type.FINISHED,
parentActivity.getString(R.string.uploads_view_group_finished_uploads)) {
@Override
public void refresh() {
fixAndSortItems(uploadsStorageManager.getFinishedUploadsForCurrentAccount());
}
};
showUser = accountManager.getAccounts().length > 1;
loadUploadItemsFromDb();
}
@Override
public void onBindViewHolder(SectionedViewHolder holder, int section, int relativePosition, int absolutePosition) {
ItemViewHolder itemViewHolder = (ItemViewHolder) holder;
OCUpload item = uploadGroups[section].getItem(relativePosition);
itemViewHolder.binding.uploadName.setText(item.getLocalPath());
// local file name
File remoteFile = new File(item.getRemotePath());
String fileName = remoteFile.getName();
if (fileName.length() == 0) {
fileName = File.separator;
}
itemViewHolder.binding.uploadName.setText(fileName);
// remote path to parent folder
itemViewHolder.binding.uploadRemotePath.setText(new File(item.getRemotePath()).getParent());
// file size
if (item.getFileSize() != 0) {
itemViewHolder.binding.uploadFileSize.setText(String.format("%s, ",
DisplayUtils.bytesToHumanReadable(item.getFileSize())));
} else {
itemViewHolder.binding.uploadFileSize.setText("");
}
// upload date
long updateTime = item.getUploadEndTimestamp();
CharSequence dateString = DisplayUtils.getRelativeDateTimeString(parentActivity,
updateTime,
DateUtils.SECOND_IN_MILLIS,
DateUtils.WEEK_IN_MILLIS, 0);
itemViewHolder.binding.uploadDate.setText(dateString);
// account
final Optional<User> optionalUser = accountManager.getUser(item.getAccountName());
if (showUser) {
itemViewHolder.binding.uploadAccount.setVisibility(View.VISIBLE);
if (optionalUser.isPresent()) {
itemViewHolder.binding.uploadAccount.setText(
DisplayUtils.getAccountNameDisplayText(optionalUser.get()));
} else {
itemViewHolder.binding.uploadAccount.setText(item.getAccountName());
}
} else {
itemViewHolder.binding.uploadAccount.setVisibility(View.GONE);
}
// Reset fields visibility
itemViewHolder.binding.uploadDate.setVisibility(View.VISIBLE);
itemViewHolder.binding.uploadRemotePath.setVisibility(View.VISIBLE);
itemViewHolder.binding.uploadFileSize.setVisibility(View.VISIBLE);
itemViewHolder.binding.uploadStatus.setVisibility(View.VISIBLE);
itemViewHolder.binding.uploadProgressBar.setVisibility(View.GONE);
// Update information depending of upload details
String status = getStatusText(item);
switch (item.getUploadStatus()) {
case UPLOAD_IN_PROGRESS -> {
viewThemeUtils.platform.themeHorizontalProgressBar(itemViewHolder.binding.uploadProgressBar);
itemViewHolder.binding.uploadProgressBar.setProgress(0);
itemViewHolder.binding.uploadProgressBar.setVisibility(View.VISIBLE);
if (uploadHelper.isUploadingNow(item)) {
// really uploading, so...
// ... unbind the old progress bar, if any; ...
if (progressListener != null) {
String targetKey = FileUploadHelper.Companion.buildRemoteName(progressListener.getUpload().getAccountName(), progressListener.getUpload().getRemotePath());
uploadHelper.removeUploadTransferProgressListener(progressListener, targetKey);
}
// ... then, bind the current progress bar to listen for updates
progressListener = new ProgressListener(item, itemViewHolder.binding.uploadProgressBar);
String targetKey = FileUploadHelper.Companion.buildRemoteName(item.getAccountName(), item.getRemotePath());
uploadHelper.addUploadTransferProgressListener(progressListener, targetKey);
} else {
// not really uploading; stop listening progress if view is reused!
if (progressListener != null &&
progressListener.isWrapping(itemViewHolder.binding.uploadProgressBar)) {
String targetKey = FileUploadHelper.Companion.buildRemoteName(progressListener.getUpload().getAccountName(), progressListener.getUpload().getRemotePath());
uploadHelper.removeUploadTransferProgressListener(progressListener, targetKey);
progressListener = null;
}
}
itemViewHolder.binding.uploadDate.setVisibility(View.GONE);
itemViewHolder.binding.uploadFileSize.setVisibility(View.GONE);
itemViewHolder.binding.uploadProgressBar.invalidate();
}
case UPLOAD_FAILED -> itemViewHolder.binding.uploadDate.setVisibility(View.GONE);
case UPLOAD_SUCCEEDED, UPLOAD_CANCELLED ->
itemViewHolder.binding.uploadStatus.setVisibility(View.GONE);
}
// show status if same file conflict or local file deleted or upload cancelled
if ((item.getUploadStatus() == UploadStatus.UPLOAD_SUCCEEDED && item.getLastResult() != UploadResult.UPLOADED)
|| item.getUploadStatus() == UploadStatus.UPLOAD_CANCELLED) {
itemViewHolder.binding.uploadStatus.setVisibility(View.VISIBLE);
itemViewHolder.binding.uploadDate.setVisibility(View.GONE);
itemViewHolder.binding.uploadFileSize.setVisibility(View.GONE);
}
itemViewHolder.binding.uploadStatus.setText(status);
// bind listeners to perform actions
if (item.getUploadStatus() == UploadStatus.UPLOAD_IN_PROGRESS) {
// Cancel
itemViewHolder.binding.uploadRightButton.setImageResource(R.drawable.ic_action_cancel_grey);
itemViewHolder.binding.uploadRightButton.setVisibility(View.VISIBLE);
itemViewHolder.binding.uploadRightButton.setOnClickListener(v -> {
uploadHelper.cancelFileUpload(item.getRemotePath(), item.getAccountName());
loadUploadItemsFromDb();
});
} else if (item.getUploadStatus() == UploadStatus.UPLOAD_FAILED) {
if (item.getLastResult() == UploadResult.SYNC_CONFLICT) {
itemViewHolder.binding.uploadRightButton.setImageResource(R.drawable.ic_dots_vertical);
itemViewHolder.binding.uploadRightButton.setOnClickListener(view -> {
if (optionalUser.isPresent()) {
User user = optionalUser.get();
showItemConflictPopup(user, itemViewHolder, item, status, view);
}
});
} else {
// Delete
itemViewHolder.binding.uploadRightButton.setImageResource(R.drawable.ic_action_delete_grey);
itemViewHolder.binding.uploadRightButton.setOnClickListener(v -> removeUpload(item));
}
itemViewHolder.binding.uploadRightButton.setVisibility(View.VISIBLE);
} else { // UploadStatus.UPLOAD_SUCCEEDED
itemViewHolder.binding.uploadRightButton.setVisibility(View.INVISIBLE);
}
itemViewHolder.binding.uploadListItemLayout.setOnClickListener(null);
// Set icon or thumbnail
itemViewHolder.binding.thumbnail.setImageResource(R.drawable.file);
// click on item
if (item.getUploadStatus() == UploadStatus.UPLOAD_FAILED ||
item.getUploadStatus() == UploadStatus.UPLOAD_CANCELLED) {
final UploadResult uploadResult = item.getLastResult();
itemViewHolder.binding.uploadListItemLayout.setOnClickListener(v -> {
if (uploadResult == UploadResult.CREDENTIAL_ERROR) {
final Optional<User> optUser = accountManager.getUser(item.getAccountName());
final User user = optUser.orElseThrow(RuntimeException::new);
parentActivity.getFileOperationsHelper().checkCurrentCredentials(user);
return;
} else if (uploadResult == UploadResult.SYNC_CONFLICT && optionalUser.isPresent()) {
User user = optionalUser.get();
if (checkAndOpenConflictResolutionDialog(user, itemViewHolder, item, status)) {
return;
}
}
// not a credentials error
File file = new File(item.getLocalPath());
Optional<User> user = accountManager.getUser(item.getAccountName());
if (file.exists() && user.isPresent()) {
uploadHelper.retryUpload(item, user.get());
loadUploadItemsFromDb();
} else {
DisplayUtils.showSnackMessage(
v.getRootView().findViewById(android.R.id.content),
R.string.local_file_not_found_message
);
}
});
} else if (item.getUploadStatus() == UploadStatus.UPLOAD_SUCCEEDED) {
itemViewHolder.binding.uploadListItemLayout.setOnClickListener(v -> onUploadedItemClick(item));
}
// click on thumbnail to open locally
if (item.getUploadStatus() != UploadStatus.UPLOAD_SUCCEEDED) {
itemViewHolder.binding.thumbnail.setOnClickListener(v -> onUploadingItemClick(item));
}
/*
* Cancellation needs do be checked and done before changing the drawable in fileIcon, or
* {@link ThumbnailsCacheManager#cancelPotentialWork} will NEVER cancel any task.
*/
OCFile fakeFileToCheatThumbnailsCacheManagerInterface = new OCFile(item.getRemotePath());
fakeFileToCheatThumbnailsCacheManagerInterface.setStoragePath(item.getLocalPath());
fakeFileToCheatThumbnailsCacheManagerInterface.setMimeType(item.getMimeType());
boolean allowedToCreateNewThumbnail = ThumbnailsCacheManager.cancelPotentialThumbnailWork(
fakeFileToCheatThumbnailsCacheManagerInterface, itemViewHolder.binding.thumbnail
);
// TODO this code is duplicated; refactor to a common place
if (MimeTypeUtil.isImage(fakeFileToCheatThumbnailsCacheManagerInterface)
&& fakeFileToCheatThumbnailsCacheManagerInterface.getRemoteId() != null &&
item.getUploadStatus() == UploadStatus.UPLOAD_SUCCEEDED) {
// Thumbnail in Cache?
Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
String.valueOf(fakeFileToCheatThumbnailsCacheManagerInterface.getRemoteId())
);
if (thumbnail != null && !fakeFileToCheatThumbnailsCacheManagerInterface.isUpdateThumbnailNeeded()) {
itemViewHolder.binding.thumbnail.setImageBitmap(thumbnail);
} else {
// generate new Thumbnail
Optional<User> user = parentActivity.getUser();
if (allowedToCreateNewThumbnail && user.isPresent()) {
final ThumbnailsCacheManager.ThumbnailGenerationTask task =
new ThumbnailsCacheManager.ThumbnailGenerationTask(
itemViewHolder.binding.thumbnail,
parentActivity.getStorageManager(),
user.get()
);
if (thumbnail == null) {
if (MimeTypeUtil.isVideo(fakeFileToCheatThumbnailsCacheManagerInterface)) {
thumbnail = ThumbnailsCacheManager.mDefaultVideo;
} else {
thumbnail = ThumbnailsCacheManager.mDefaultImg;
}
}
final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable =
new ThumbnailsCacheManager.AsyncThumbnailDrawable(
parentActivity.getResources(),
thumbnail,
task
);
itemViewHolder.binding.thumbnail.setImageDrawable(asyncDrawable);
task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(
fakeFileToCheatThumbnailsCacheManagerInterface, null));
}
}
if ("image/png".equals(item.getMimeType())) {
itemViewHolder.binding.thumbnail.setBackgroundColor(parentActivity.getResources()
.getColor(R.color.bg_default));
}
} else if (MimeTypeUtil.isImage(fakeFileToCheatThumbnailsCacheManagerInterface)) {
File file = new File(item.getLocalPath());
// Thumbnail in Cache?
Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
String.valueOf(file.hashCode()));
if (thumbnail != null) {
itemViewHolder.binding.thumbnail.setImageBitmap(thumbnail);
} else {
// generate new Thumbnail
if (allowedToCreateNewThumbnail) {
final ThumbnailsCacheManager.ThumbnailGenerationTask task =
new ThumbnailsCacheManager.ThumbnailGenerationTask(itemViewHolder.binding.thumbnail);
if (MimeTypeUtil.isVideo(file)) {
thumbnail = ThumbnailsCacheManager.mDefaultVideo;
} else {
thumbnail = ThumbnailsCacheManager.mDefaultImg;
}
final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable =
new ThumbnailsCacheManager.AsyncThumbnailDrawable(parentActivity.getResources(), thumbnail,
task);
itemViewHolder.binding.thumbnail.setImageDrawable(asyncDrawable);
task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, null));
Log_OC.v(TAG, "Executing task to generate a new thumbnail");
}
}
if ("image/png".equalsIgnoreCase(item.getMimeType())) {
itemViewHolder.binding.thumbnail.setBackgroundColor(parentActivity.getResources()
.getColor(R.color.bg_default));
}
} else {
if (optionalUser.isPresent()) {
final User user = optionalUser.get();
final Drawable icon = MimeTypeUtil.getFileTypeIcon(item.getMimeType(),
fileName,
parentActivity,
viewThemeUtils);
itemViewHolder.binding.thumbnail.setImageDrawable(icon);
}
}
}
private boolean checkAndOpenConflictResolutionDialog(User user,
ItemViewHolder itemViewHolder,
OCUpload item,
String status) {
String remotePath = item.getRemotePath();
OCFile localFile = storageManager.getFileByEncryptedRemotePath(remotePath);
if (localFile == null) {
// Remote file doesn't exist, try to refresh folder
OCFile folder = storageManager.getFileByEncryptedRemotePath(new File(remotePath).getParent() + "/");
if (folder != null && folder.isFolder()) {
refreshFolderAndUpdateUI(itemViewHolder, user, folder, remotePath, item, status);
return true;
}
// Destination folder doesn't exist anymore
}
if (localFile != null) {
this.openConflictActivity(localFile, item);
return true;
}
// Remote file doesn't exist anymore = there is no more conflict
return false;
}
private void refreshFolderAndUpdateUI(ItemViewHolder holder, User user, OCFile folder, String remotePath, OCUpload item, String status) {
Context context = MainApp.getAppContext();
this.refreshFolder(context, holder, user, folder, (caller, result) -> {
holder.binding.uploadStatus.setText(status);
if (result.isSuccess()) {
OCFile fileOnServer = storageManager.getFileByEncryptedRemotePath(remotePath);
if (fileOnServer != null) {
openConflictActivity(fileOnServer, item);
} else {
displayFileNotFoundError(holder.itemView, context);
}
}
});
}
private void displayFileNotFoundError(View itemView, Context context) {
String message = context.getString(R.string.uploader_file_not_found_message);
DisplayUtils.showSnackMessage(itemView, message);
}
private void showItemConflictPopup(User user,
ItemViewHolder itemViewHolder,
OCUpload item,
String status,
View view) {
PopupMenu popup = new PopupMenu(MainApp.getAppContext(), view);
popup.inflate(R.menu.upload_list_item_file_conflict);
popup.setOnMenuItemClickListener(i -> {
int itemId = i.getItemId();
if (itemId == R.id.action_upload_list_resolve_conflict) {
checkAndOpenConflictResolutionDialog(user, itemViewHolder, item, status);
} else {
removeUpload(item);
}
return true;
});
popup.show();
}
public void removeUpload(OCUpload item) {
uploadsStorageManager.removeUpload(item);
cancelOldErrorNotification(item);
loadUploadItemsFromDb();
}
private void refreshFolder(
Context context,
ItemViewHolder view,
User user,
OCFile folder,
OnRemoteOperationListener listener) {
view.binding.uploadListItemLayout.setClickable(false);
view.binding.uploadStatus.setText(R.string.uploads_view_upload_status_fetching_server_version);
new RefreshFolderOperation(folder,
clock.getCurrentTime(),
false,
false,
true,
storageManager,
user,
context)
.execute(user, context, (caller, result) -> {
view.binding.uploadListItemLayout.setClickable(true);
listener.onRemoteOperationFinish(caller, result);
}, parentActivity.getHandler());
}
private void openConflictActivity(OCFile file, OCUpload upload) {
file.setStoragePath(upload.getLocalPath());
Context context = MainApp.getAppContext();
Optional<User> user = accountManager.getUser(upload.getAccountName());
if (user.isPresent()) {
Intent intent = ConflictsResolveActivity.createIntent(file,
user.get(),
upload.getUploadId(),
Intent.FLAG_ACTIVITY_NEW_TASK,
context);
context.startActivity(intent);
}
}
/**
* Gets the status text to show to the user according to the status and last result of the the given upload.
*
* @param upload Upload to describe.
* @return Text describing the status of the given upload.
*/
private String getStatusText(OCUpload upload) {
String status;
switch (upload.getUploadStatus()) {
case UPLOAD_IN_PROGRESS -> {
status = parentActivity.getString(R.string.uploads_view_later_waiting_to_upload);
if (uploadHelper.isUploadingNow(upload)) {
// really uploading, bind the progress bar to listen for progress updates
status = parentActivity.getString(R.string.uploader_upload_in_progress_ticker);
}
if (parentActivity.getAppPreferences().isGlobalUploadPaused()) {
status = parentActivity.getString(R.string.upload_global_pause_title);
}
}
case UPLOAD_SUCCEEDED -> {
if (upload.getLastResult() == UploadResult.SAME_FILE_CONFLICT) {
status = parentActivity.getString(R.string.uploads_view_upload_status_succeeded_same_file);
} else if (upload.getLastResult() == UploadResult.FILE_NOT_FOUND) {
status = getUploadFailedStatusText(upload.getLastResult());
} else {
status = parentActivity.getString(R.string.uploads_view_upload_status_succeeded);
}
}
case UPLOAD_FAILED -> {
status = getUploadFailedStatusText(upload.getLastResult());
}
case UPLOAD_CANCELLED -> {
status = parentActivity.getString(R.string.upload_manually_cancelled);
}
default -> {
status = "Uncontrolled status: " + upload.getUploadStatus();
}
}
return status;
}
@NonNull
private String getUploadFailedStatusText(UploadResult result) {
String status;
switch (result) {
case CREDENTIAL_ERROR:
status = parentActivity.getString(R.string.uploads_view_upload_status_failed_credentials_error);
break;
case FOLDER_ERROR:
status = parentActivity.getString(R.string.uploads_view_upload_status_failed_folder_error);
break;
case FILE_NOT_FOUND:
status = parentActivity.getString(R.string.uploads_view_upload_status_failed_localfile_error);
break;
case FILE_ERROR:
status = parentActivity.getString(R.string.uploads_view_upload_status_failed_file_error);
break;
case PRIVILEGES_ERROR:
status = parentActivity.getString(R.string.uploads_view_upload_status_failed_permission_error);
break;
case NETWORK_CONNECTION:
status = parentActivity.getString(R.string.uploads_view_upload_status_failed_connection_error);
break;
case DELAYED_FOR_WIFI:
status = parentActivity.getString(R.string.uploads_view_upload_status_waiting_for_wifi);
break;
case DELAYED_FOR_CHARGING:
status = parentActivity.getString(R.string.uploads_view_upload_status_waiting_for_charging);
break;
case CONFLICT_ERROR:
status = parentActivity.getString(R.string.uploads_view_upload_status_conflict);
break;
case SERVICE_INTERRUPTED:
status = parentActivity.getString(R.string.uploads_view_upload_status_service_interrupted);
break;
case CANCELLED:
// should not get here ; cancelled uploads should be wiped out
status = parentActivity.getString(R.string.uploads_view_upload_status_cancelled);
break;
case UPLOADED:
// should not get here ; status should be UPLOAD_SUCCESS
status = parentActivity.getString(R.string.uploads_view_upload_status_succeeded);
break;
case MAINTENANCE_MODE:
status = parentActivity.getString(R.string.maintenance_mode);
break;
case SSL_RECOVERABLE_PEER_UNVERIFIED:
status =
parentActivity.getString(
R.string.uploads_view_upload_status_failed_ssl_certificate_not_trusted
);
break;
case UNKNOWN:
status = parentActivity.getString(R.string.uploads_view_upload_status_unknown_fail);
break;
case LOCK_FAILED:
status = parentActivity.getString(R.string.upload_lock_failed);
break;
case DELAYED_IN_POWER_SAVE_MODE:
status = parentActivity.getString(
R.string.uploads_view_upload_status_waiting_exit_power_save_mode);
break;
case VIRUS_DETECTED:
status = parentActivity.getString(R.string.uploads_view_upload_status_virus_detected);
break;
case LOCAL_STORAGE_FULL:
status = parentActivity.getString(R.string.upload_local_storage_full);
break;
case OLD_ANDROID_API:
status = parentActivity.getString(R.string.upload_old_android);
break;
case SYNC_CONFLICT:
status = parentActivity.getString(R.string.upload_sync_conflict);
break;
case CANNOT_CREATE_FILE:
status = parentActivity.getString(R.string.upload_cannot_create_file);
break;
case LOCAL_STORAGE_NOT_COPIED:
status = parentActivity.getString(R.string.upload_local_storage_not_copied);
break;
case QUOTA_EXCEEDED:
status = parentActivity.getString(R.string.upload_quota_exceeded);
break;
default:
status = parentActivity.getString(R.string.upload_unknown_error);
break;
}
return status;
}
@Override
@NonNull
public SectionedViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_HEADER) {
return new HeaderViewHolder(
UploadListHeaderBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
);
} else {
return new ItemViewHolder(
UploadListItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
);
}
}
/**
* Load upload items from {@link UploadsStorageManager}.
*/
public final void loadUploadItemsFromDb() {
Log_OC.d(TAG, "loadUploadItemsFromDb");
for (UploadGroup group : uploadGroups) {
group.refresh();
}
notifyDataSetChanged();
}
/**
* Open local file.
*/
private void onUploadingItemClick(OCUpload file) {
File f = new File(file.getLocalPath());
if (!f.exists()) {
DisplayUtils.showSnackMessage(parentActivity, R.string.local_file_not_found_message);
} else {
openFileWithDefault(file.getLocalPath());
}
}
/**
* Open remote file.
*/
private void onUploadedItemClick(OCUpload upload) {
final OCFile file = parentActivity.getStorageManager().getFileByEncryptedRemotePath(upload.getRemotePath());
if (file == null) {
DisplayUtils.showSnackMessage(parentActivity, R.string.error_retrieving_file);
Log_OC.i(TAG, "Could not find uploaded file on remote.");
return;
}
if (PreviewImageFragment.canBePreviewed(file)) {
//show image preview and stay in uploads tab
Intent intent = FileDisplayActivity.openFileIntent(parentActivity, parentActivity.getUser().get(), file);
parentActivity.startActivity(intent);
} else {
Intent intent = new Intent(parentActivity, FileDisplayActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(FileDisplayActivity.KEY_FILE_PATH, upload.getRemotePath());
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
parentActivity.startActivity(intent);
}
}
/**
* Open file with app associates with its MIME type. If MIME type unknown, show list with all apps.
*/
private void openFileWithDefault(String localPath) {
Intent myIntent = new Intent(Intent.ACTION_VIEW);
File file = new File(localPath);
String mimetype = MimeTypeUtil.getBestMimeTypeByFilename(localPath);
if ("application/octet-stream".equals(mimetype)) {
mimetype = "*/*";
}
myIntent.setDataAndType(Uri.fromFile(file), mimetype);
try {
parentActivity.startActivity(myIntent);
} catch (ActivityNotFoundException e) {
DisplayUtils.showSnackMessage(parentActivity, R.string.file_list_no_app_for_file_type);
Log_OC.i(TAG, "Could not find app for sending log history.");
}
}
static class HeaderViewHolder extends SectionedViewHolder {
UploadListHeaderBinding binding;
HeaderViewHolder(UploadListHeaderBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
static class ItemViewHolder extends SectionedViewHolder {
UploadListItemBinding binding;
ItemViewHolder(UploadListItemBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
interface Refresh {
void refresh();
}
enum Type {
CURRENT, FINISHED, FAILED, CANCELLED
}
abstract class UploadGroup implements Refresh {
private Type type;
private OCUpload[] items;
private String name;
UploadGroup(Type type, String groupName) {
this.type = type;
this.name = groupName;
items = new OCUpload[0];
}
private String getGroupName() {
return name;
}
public OCUpload[] getItems() {
return items;
}
public OCUpload getItem(int position) {
return items[position];
}
public void setItems(OCUpload... items) {
this.items = items;
}
void fixAndSortItems(OCUpload... array) {
for (OCUpload upload : array) {
upload.setDataFixed(uploadHelper);
}
Arrays.sort(array, new OCUploadComparator());
setItems(array);
}
private int getGroupItemCount() {
return items == null ? 0 : items.length;
}
}
public void cancelOldErrorNotification(OCUpload upload) {
if (mNotificationManager == null) {
mNotificationManager = (NotificationManager) parentActivity.getSystemService(parentActivity.NOTIFICATION_SERVICE);
}
if (upload == null) {
return;
}
mNotificationManager.cancel(NotificationUtils.createUploadNotificationTag(upload.getRemotePath(), upload.getLocalPath()),
FileUploadWorker.NOTIFICATION_ERROR_ID);
}
}