Merge pull request #12121 from nextcloud/chore/testRollback

test replacing kt with java version
This commit is contained in:
Andy Scherzinger 2023-11-01 15:36:07 +01:00 committed by GitHub
commit 8f6936f0f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 745 additions and 754 deletions

View File

@ -0,0 +1,741 @@
/*
* ownCloud Android client application
*
* Copyright (C) 2012 Bartek Przybylski
* Copyright (C) 2012-2016 ownCloud Inc.
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
package com.owncloud.android.files.services;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.OnAccountsUpdateListener;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.util.Pair;
import com.nextcloud.client.account.User;
import com.nextcloud.client.account.UserAccountManager;
import com.nextcloud.client.files.downloader.DownloadTask;
import com.nextcloud.java.util.Optional;
import com.owncloud.android.R;
import com.owncloud.android.authentication.AuthenticatorActivity;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.datamodel.UploadsStorageManager;
import com.owncloud.android.lib.common.OwnCloudAccount;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
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.common.utils.Log_OC;
import com.owncloud.android.lib.resources.files.FileUtils;
import com.owncloud.android.operations.DownloadFileOperation;
import com.owncloud.android.operations.DownloadType;
import com.owncloud.android.providers.DocumentsStorageProvider;
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.dialog.SendShareDialog;
import com.owncloud.android.ui.fragment.OCFileListFragment;
import com.owncloud.android.ui.notifications.NotificationUtils;
import com.owncloud.android.ui.preview.PreviewImageActivity;
import com.owncloud.android.ui.preview.PreviewImageFragment;
import com.owncloud.android.utils.ErrorMessageAdapter;
import com.owncloud.android.utils.MimeTypeUtil;
import com.owncloud.android.utils.theme.ViewThemeUtils;
import java.io.File;
import java.security.SecureRandom;
import java.util.AbstractList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
import javax.inject.Inject;
import androidx.core.app.NotificationCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import dagger.android.AndroidInjection;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
public class FileDownloader extends Service
implements OnDatatransferProgressListener, OnAccountsUpdateListener {
public static final String EXTRA_USER = "USER";
public static final String EXTRA_FILE = "FILE";
private static final String DOWNLOAD_ADDED_MESSAGE = "DOWNLOAD_ADDED";
private static final String DOWNLOAD_FINISH_MESSAGE = "DOWNLOAD_FINISH";
public static final String EXTRA_DOWNLOAD_RESULT = "RESULT";
public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH";
public static final String EXTRA_LINKED_TO_PATH = "LINKED_TO";
public static final String ACCOUNT_NAME = "ACCOUNT_NAME";
public static final String DOWNLOAD_TYPE = "DOWNLOAD_TYPE";
private static final int FOREGROUND_SERVICE_ID = 412;
private static final String TAG = FileDownloader.class.getSimpleName();
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
private IBinder mBinder;
private OwnCloudClient mDownloadClient;
private Optional<User> currentUser = Optional.empty();
private FileDataStorageManager mStorageManager;
private IndexedForest<DownloadFileOperation> mPendingDownloads = new IndexedForest<>();
private DownloadFileOperation mCurrentDownload;
private NotificationManager mNotificationManager;
private NotificationCompat.Builder mNotificationBuilder;
private int mLastPercent;
private Notification mNotification;
private long conflictUploadId;
public boolean mStartedDownload = false;
@Inject UserAccountManager accountManager;
@Inject UploadsStorageManager uploadsStorageManager;
@Inject LocalBroadcastManager localBroadcastManager;
@Inject ViewThemeUtils viewThemeUtils;
public static String getDownloadAddedMessage() {
return FileDownloader.class.getName() + DOWNLOAD_ADDED_MESSAGE;
}
public static String getDownloadFinishMessage() {
return FileDownloader.class.getName() + DOWNLOAD_FINISH_MESSAGE;
}
/**
* Service initialization
*/
@Override
public void onCreate() {
super.onCreate();
AndroidInjection.inject(this);
Log_OC.d(TAG, "Creating service");
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
HandlerThread thread = new HandlerThread("FileDownloaderThread", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper, this);
mBinder = new FileDownloaderBinder();
NotificationCompat.Builder builder = NotificationUtils.newNotificationBuilder(this, viewThemeUtils).setContentTitle(
getApplicationContext().getResources().getString(R.string.app_name))
.setContentText(getApplicationContext().getResources().getString(R.string.foreground_service_download))
.setSmallIcon(R.drawable.notification_icon)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.notification_icon));
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
builder.setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_DOWNLOAD);
}
mNotification = builder.build();
// add AccountsUpdatedListener
AccountManager am = AccountManager.get(getApplicationContext());
am.addOnAccountsUpdatedListener(this, null, false);
}
/**
* Service clean up
*/
@Override
public void onDestroy() {
Log_OC.v(TAG, "Destroying service");
mBinder = null;
mServiceHandler = null;
mServiceLooper.quit();
mServiceLooper = null;
mNotificationManager = null;
// remove AccountsUpdatedListener
AccountManager am = AccountManager.get(getApplicationContext());
am.removeOnAccountsUpdatedListener(this);
super.onDestroy();
}
/**
* Entry point to add one or several files to the queue of downloads.
*
* New downloads 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) {
Log_OC.d(TAG, "Starting command with id " + startId);
startForeground(FOREGROUND_SERVICE_ID, mNotification);
if (intent == null || !intent.hasExtra(EXTRA_USER) || !intent.hasExtra(EXTRA_FILE)) {
Log_OC.e(TAG, "Not enough information provided in intent");
return START_NOT_STICKY;
} else {
final User user = intent.getParcelableExtra(EXTRA_USER);
final OCFile file = intent.getParcelableExtra(EXTRA_FILE);
final String behaviour = intent.getStringExtra(OCFileListFragment.DOWNLOAD_BEHAVIOUR);
DownloadType downloadType = DownloadType.DOWNLOAD;
if (intent.hasExtra(DOWNLOAD_TYPE)) {
downloadType = (DownloadType) intent.getSerializableExtra(DOWNLOAD_TYPE);
}
String activityName = intent.getStringExtra(SendShareDialog.ACTIVITY_NAME);
String packageName = intent.getStringExtra(SendShareDialog.PACKAGE_NAME);
conflictUploadId = intent.getLongExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD_ID, -1);
AbstractList<String> requestedDownloads = new Vector<String>();
try {
DownloadFileOperation newDownload = new DownloadFileOperation(user,
file,
behaviour,
activityName,
packageName,
getBaseContext(),
downloadType);
newDownload.addDatatransferProgressListener(this);
newDownload.addDatatransferProgressListener((FileDownloaderBinder) mBinder);
Pair<String, String> putResult = mPendingDownloads.putIfAbsent(user.getAccountName(),
file.getRemotePath(),
newDownload);
if (putResult != null) {
String downloadKey = putResult.first;
requestedDownloads.add(downloadKey);
sendBroadcastNewDownload(newDownload, putResult.second);
} // else, file already in the queue of downloads; don't repeat the request
} catch (IllegalArgumentException e) {
Log_OC.e(TAG, "Not enough information provided in intent: " + e.getMessage());
return START_NOT_STICKY;
}
if (requestedDownloads.size() > 0) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = requestedDownloads;
mServiceHandler.sendMessage(msg);
}
}
return START_NOT_STICKY;
}
/**
* Provides a binder object that clients can use to perform operations on the queue of downloads,
* excepting the addition of new files.
*
* Implemented to perform cancellation, pause and resume of existing downloads.
*/
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
/**
* Called when ALL the bound clients were onbound.
*/
@Override
public boolean onUnbind(Intent intent) {
((FileDownloaderBinder) mBinder).clearListeners();
return false; // not accepting rebinding (default behaviour)
}
@Override
public void onAccountsUpdated(Account[] accounts) {
//review the current download and cancel it if its account doesn't exist
if (mCurrentDownload != null && !accountManager.exists(mCurrentDownload.getUser().toPlatformAccount())) {
mCurrentDownload.cancel();
}
// The rest of downloads are cancelled when they try to start
}
/**
* Binder to let client components to perform operations on the queue of downloads.
* <p/>
* It provides by itself the available operations.
*/
public class FileDownloaderBinder extends Binder implements OnDatatransferProgressListener {
/**
* Map of listeners that will be reported about progress of downloads from a
* {@link FileDownloaderBinder}
* instance.
*/
private Map<Long, OnDatatransferProgressListener> mBoundListeners =
new HashMap<Long, OnDatatransferProgressListener>();
/**
* Cancels a pending or current download of a remote file.
*
* @param account ownCloud account where the remote file is stored.
* @param file A file in the queue of pending downloads
*/
public void cancel(Account account, OCFile file) {
Pair<DownloadFileOperation, String> removeResult =
mPendingDownloads.remove(account.name, file.getRemotePath());
DownloadFileOperation download = removeResult.first;
if (download != null) {
download.cancel();
} else {
if (mCurrentDownload != null && currentUser.isPresent() &&
mCurrentDownload.getRemotePath().startsWith(file.getRemotePath()) &&
account.name.equals(currentUser.get().getAccountName())) {
mCurrentDownload.cancel();
}
}
}
/**
* Cancels all the downloads for an account
*/
public void cancel(String accountName) {
if (mCurrentDownload != null && mCurrentDownload.getUser().nameEquals(accountName)) {
mCurrentDownload.cancel();
}
// Cancel pending downloads
cancelPendingDownloads(accountName);
}
public void clearListeners() {
mBoundListeners.clear();
}
/**
* Returns True when the file described by 'file' in the ownCloud account 'account'
* is downloading or waiting to download.
*
* If 'file' is a directory, returns 'true' if any of its descendant files is downloading or
* waiting to download.
*
* @param user user where the remote file is stored.
* @param file A file that could be in the queue of downloads.
*/
public boolean isDownloading(User user, OCFile file) {
return user != null && file != null && mPendingDownloads.contains(user.getAccountName(), file.getRemotePath());
}
/**
* Adds a listener interested in the progress of the download for a concrete file.
*
* @param listener Object to notify about progress of transfer.
* @param file {@link OCFile} of interest for listener.
*/
public void addDatatransferProgressListener(OnDatatransferProgressListener listener, OCFile file) {
if (file == null || listener == null) {
return;
}
mBoundListeners.put(file.getFileId(), listener);
}
/**
* Removes a listener interested in the progress of the download for a concrete file.
*
* @param listener Object to notify about progress of transfer.
* @param file {@link OCFile} of interest for listener.
*/
public void removeDatatransferProgressListener(OnDatatransferProgressListener listener, OCFile file) {
if (file == null || listener == null) {
return;
}
Long fileId = file.getFileId();
if (mBoundListeners.get(fileId) == listener) {
mBoundListeners.remove(fileId);
}
}
@Override
public void onTransferProgress(long progressRate, long totalTransferredSoFar,
long totalToTransfer, String fileName) {
OnDatatransferProgressListener boundListener =
mBoundListeners.get(mCurrentDownload.getFile().getFileId());
if (boundListener != null) {
boundListener.onTransferProgress(progressRate, totalTransferredSoFar,
totalToTransfer, fileName);
}
}
}
/**
* Download worker. Performs the pending downloads in the order they were requested.
* Created with the Looper of a new thread, started in {@link FileUploader#onCreate()}.
*/
private static class ServiceHandler extends Handler {
// don't make it a final class, and don't remove the static ; lint will warn about a
// possible memory leak
FileDownloader mService;
public ServiceHandler(Looper looper, FileDownloader service) {
super(looper);
if (service == null) {
throw new IllegalArgumentException("Received invalid NULL in parameter 'service'");
}
mService = service;
}
@Override
public void handleMessage(Message msg) {
@SuppressWarnings("unchecked")
AbstractList<String> requestedDownloads = (AbstractList<String>) msg.obj;
if (msg.obj != null) {
Iterator<String> it = requestedDownloads.iterator();
while (it.hasNext()) {
String next = it.next();
mService.downloadFile(next);
}
}
mService.mStartedDownload=false;
(new Handler()).postDelayed(() -> {
if(!mService.mStartedDownload){
mService.mNotificationManager.cancel(R.string.downloader_download_in_progress_ticker);
}
Log_OC.d(TAG, "Stopping after command with id " + msg.arg1);
mService.mNotificationManager.cancel(FOREGROUND_SERVICE_ID);
mService.stopForeground(true);
mService.stopSelf(msg.arg1);
}, 2000);
}
}
/**
* Core download method: requests a file to download and stores it.
*
* @param downloadKey Key to access the download to perform, contained in mPendingDownloads
*/
private void downloadFile(String downloadKey) {
mStartedDownload = true;
mCurrentDownload = mPendingDownloads.get(downloadKey);
if (mCurrentDownload != null) {
// Detect if the account exists
if (accountManager.exists(mCurrentDownload.getUser().toPlatformAccount())) {
notifyDownloadStart(mCurrentDownload);
RemoteOperationResult downloadResult = null;
try {
/// prepare client object to send the request to the ownCloud server
Account currentDownloadAccount = mCurrentDownload.getUser().toPlatformAccount();
Optional<User> currentDownloadUser = accountManager.getUser(currentDownloadAccount.name);
if (!currentUser.equals(currentDownloadUser)) {
currentUser = currentDownloadUser;
mStorageManager = new FileDataStorageManager(currentUser.get(), getContentResolver());
} // else, reuse storage manager from previous operation
// always get client from client manager, to get fresh credentials in case
// of update
OwnCloudAccount ocAccount = currentDownloadUser.get().toOwnCloudAccount();
mDownloadClient = OwnCloudClientManagerFactory.getDefaultSingleton().
getClientFor(ocAccount, this);
/// perform the download
downloadResult = mCurrentDownload.execute(mDownloadClient);
if (downloadResult.isSuccess() && mCurrentDownload.getDownloadType() == DownloadType.DOWNLOAD) {
saveDownloadedFile();
}
} catch (Exception e) {
Log_OC.e(TAG, "Error downloading", e);
downloadResult = new RemoteOperationResult(e);
} finally {
Pair<DownloadFileOperation, String> removeResult = mPendingDownloads.removePayload(
mCurrentDownload.getUser().getAccountName(), mCurrentDownload.getRemotePath());
if (downloadResult == null) {
downloadResult = new RemoteOperationResult(new RuntimeException("Error downloading…"));
}
/// notify result
notifyDownloadResult(mCurrentDownload, downloadResult);
sendBroadcastDownloadFinished(mCurrentDownload, downloadResult, removeResult.second);
}
} else {
cancelPendingDownloads(mCurrentDownload.getUser().getAccountName());
}
}
}
/**
* Updates the OC File after a successful download.
*
* TODO move to DownloadFileOperation
* unify with code from {@link DocumentsStorageProvider} and {@link DownloadTask}.
*/
private void saveDownloadedFile() {
OCFile file = mStorageManager.getFileById(mCurrentDownload.getFile().getFileId());
if (file == null) {
// try to get file via path, needed for overwriting existing files on conflict dialog
file = mStorageManager.getFileByDecryptedRemotePath(mCurrentDownload.getFile().getRemotePath());
}
if (file == null) {
Log_OC.e(this, "Could not save " + mCurrentDownload.getFile().getRemotePath());
return;
}
long syncDate = System.currentTimeMillis();
file.setLastSyncDateForProperties(syncDate);
file.setLastSyncDateForData(syncDate);
file.setUpdateThumbnailNeeded(true);
file.setModificationTimestamp(mCurrentDownload.getModificationTimestamp());
file.setModificationTimestampAtLastSyncForData(mCurrentDownload.getModificationTimestamp());
file.setEtag(mCurrentDownload.getEtag());
file.setMimeType(mCurrentDownload.getMimeType());
file.setStoragePath(mCurrentDownload.getSavePath());
file.setFileLength(new File(mCurrentDownload.getSavePath()).length());
file.setRemoteId(mCurrentDownload.getFile().getRemoteId());
mStorageManager.saveFile(file);
if (MimeTypeUtil.isMedia(mCurrentDownload.getMimeType())) {
FileDataStorageManager.triggerMediaScan(file.getStoragePath(), file);
}
mStorageManager.saveConflict(file, null);
}
/**
* Creates a status notification to show the download progress
*
* @param download Download operation starting.
*/
private void notifyDownloadStart(DownloadFileOperation download) {
/// create status notification with a progress bar
mLastPercent = 0;
mNotificationBuilder = NotificationUtils.newNotificationBuilder(this, viewThemeUtils);
mNotificationBuilder
.setSmallIcon(R.drawable.notification_icon)
.setTicker(getString(R.string.downloader_download_in_progress_ticker))
.setContentTitle(getString(R.string.downloader_download_in_progress_ticker))
.setOngoing(true)
.setProgress(100, 0, download.getSize() < 0)
.setContentText(
String.format(getString(R.string.downloader_download_in_progress_content), 0,
new File(download.getSavePath()).getName())
);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
mNotificationBuilder.setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_DOWNLOAD);
}
/// includes a pending intent in the notification showing the details view of the file
Intent showDetailsIntent = null;
if (PreviewImageFragment.canBePreviewed(download.getFile())) {
showDetailsIntent = new Intent(this, PreviewImageActivity.class);
} else {
showDetailsIntent = new Intent(this, FileDisplayActivity.class);
}
showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, download.getFile());
showDetailsIntent.putExtra(FileActivity.EXTRA_USER, download.getUser());
showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
mNotificationBuilder.setContentIntent(PendingIntent.getActivity(this, (int) System.currentTimeMillis(),
showDetailsIntent, PendingIntent.FLAG_IMMUTABLE));
if (mNotificationManager == null) {
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
}
if (mNotificationManager != null) {
mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotificationBuilder.build());
}
}
/**
* 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, totalToTransfer < 0);
String fileName = filePath.substring(filePath.lastIndexOf(FileUtils.PATH_SEPARATOR) + 1);
String text = String.format(getString(R.string.downloader_download_in_progress_content), percent, fileName);
mNotificationBuilder.setContentText(text);
if (mNotificationManager == null) {
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
}
if (mNotificationManager != null) {
mNotificationManager.notify(R.string.downloader_download_in_progress_ticker,
mNotificationBuilder.build());
}
}
mLastPercent = percent;
}
/**
* Updates the status notification with the result of a download operation.
*
* @param downloadResult Result of the download operation.
* @param download Finished download operation
*/
@SuppressFBWarnings("DMI")
private void notifyDownloadResult(DownloadFileOperation download,
RemoteOperationResult downloadResult) {
if (mNotificationManager == null) {
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
}
if (!downloadResult.isCancelled()) {
if (downloadResult.isSuccess()) {
if (conflictUploadId > 0) {
uploadsStorageManager.removeUpload(conflictUploadId);
}
// Dont show notification except an error has occured.
return;
}
int tickerId = downloadResult.isSuccess() ?
R.string.downloader_download_succeeded_ticker : R.string.downloader_download_failed_ticker;
boolean needsToUpdateCredentials = ResultCode.UNAUTHORIZED == downloadResult.getCode();
tickerId = needsToUpdateCredentials ?
R.string.downloader_download_failed_credentials_error : tickerId;
mNotificationBuilder
.setTicker(getString(tickerId))
.setContentTitle(getString(tickerId))
.setAutoCancel(true)
.setOngoing(false)
.setProgress(0, 0, false);
if (needsToUpdateCredentials) {
configureUpdateCredentialsNotification(download.getUser());
} else {
// TODO put something smart in showDetailsIntent
Intent showDetailsIntent = new Intent();
mNotificationBuilder.setContentIntent(PendingIntent.getActivity(this, (int) System.currentTimeMillis(),
showDetailsIntent, PendingIntent.FLAG_IMMUTABLE));
}
mNotificationBuilder.setContentText(ErrorMessageAdapter.getErrorCauseMessage(downloadResult,
download, getResources()));
if (mNotificationManager != null) {
mNotificationManager.notify((new SecureRandom()).nextInt(), mNotificationBuilder.build());
// Remove success notification
if (downloadResult.isSuccess()) {
// Sleep 2 seconds, so show the notification before remove it
NotificationUtils.cancelWithDelay(mNotificationManager,
R.string.downloader_download_succeeded_ticker, 2000);
}
}
}
}
private void configureUpdateCredentialsNotification(User user) {
// let the user update credentials with one click
Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, user.toPlatformAccount());
updateAccountCredentials.putExtra(
AuthenticatorActivity.EXTRA_ACTION,
AuthenticatorActivity.ACTION_UPDATE_EXPIRED_TOKEN
);
updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND);
mNotificationBuilder.setContentIntent(
PendingIntent.getActivity(this,
(int) System.currentTimeMillis(),
updateAccountCredentials,
PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE)
);
}
/**
* Sends a broadcast when a download finishes in order to the interested activities can
* update their view
*
* @param download Finished download operation
* @param downloadResult Result of the download operation
* @param unlinkedFromRemotePath Path in the downloads tree where the download was unlinked from
*/
private void sendBroadcastDownloadFinished(
DownloadFileOperation download,
RemoteOperationResult downloadResult,
String unlinkedFromRemotePath) {
Intent end = new Intent(getDownloadFinishMessage());
end.putExtra(EXTRA_DOWNLOAD_RESULT, downloadResult.isSuccess());
end.putExtra(ACCOUNT_NAME, download.getUser().getAccountName());
end.putExtra(EXTRA_REMOTE_PATH, download.getRemotePath());
end.putExtra(OCFileListFragment.DOWNLOAD_BEHAVIOUR, download.getBehaviour());
end.putExtra(SendShareDialog.ACTIVITY_NAME, download.getActivityName());
end.putExtra(SendShareDialog.PACKAGE_NAME, download.getPackageName());
if (unlinkedFromRemotePath != null) {
end.putExtra(EXTRA_LINKED_TO_PATH, unlinkedFromRemotePath);
}
end.setPackage(getPackageName());
localBroadcastManager.sendBroadcast(end);
}
/**
* Sends a broadcast when a new download is added to the queue.
*
* @param download Added download operation
* @param linkedToRemotePath Path in the downloads tree where the download was linked to
*/
private void sendBroadcastNewDownload(DownloadFileOperation download,
String linkedToRemotePath) {
Intent added = new Intent(getDownloadAddedMessage());
added.putExtra(ACCOUNT_NAME, download.getUser().getAccountName());
added.putExtra(EXTRA_REMOTE_PATH, download.getRemotePath());
added.putExtra(EXTRA_LINKED_TO_PATH, linkedToRemotePath);
added.setPackage(getPackageName());
localBroadcastManager.sendBroadcast(added);
}
private void cancelPendingDownloads(String accountName) {
mPendingDownloads.remove(accountName);
}
}

View File

@ -1,750 +0,0 @@
/*
* ownCloud Android client application
*
* Copyright (C) 2012 Bartek Przybylski
* Copyright (C) 2012-2016 ownCloud Inc.
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
package com.owncloud.android.files.services
import android.accounts.Account
import android.accounts.AccountManager
import android.accounts.OnAccountsUpdateListener
import android.app.Notification
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.Process
import androidx.core.app.NotificationCompat
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.nextcloud.client.account.User
import com.nextcloud.client.account.UserAccountManager
import com.nextcloud.java.util.Optional
import com.owncloud.android.R
import com.owncloud.android.authentication.AuthenticatorActivity
import com.owncloud.android.datamodel.FileDataStorageManager
import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.datamodel.UploadsStorageManager
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory
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.common.utils.Log_OC
import com.owncloud.android.operations.DownloadFileOperation
import com.owncloud.android.operations.DownloadType
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.dialog.SendShareDialog
import com.owncloud.android.ui.fragment.OCFileListFragment
import com.owncloud.android.ui.notifications.NotificationUtils
import com.owncloud.android.ui.preview.PreviewImageActivity
import com.owncloud.android.ui.preview.PreviewImageFragment
import com.owncloud.android.utils.ErrorMessageAdapter
import com.owncloud.android.utils.MimeTypeUtil
import com.owncloud.android.utils.theme.ViewThemeUtils
import dagger.android.AndroidInjection
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import java.io.File
import java.security.SecureRandom
import java.util.AbstractList
import javax.inject.Inject
class FileDownloader : Service(), OnDatatransferProgressListener, OnAccountsUpdateListener {
private var mServiceLooper: Looper? = null
private var mServiceHandler: ServiceHandler? = null
private var mBinder: IBinder? = null
private var mDownloadClient: OwnCloudClient? = null
private var currentUser = Optional.empty<User>()
private var mStorageManager: FileDataStorageManager? = null
private val mPendingDownloads = IndexedForest<DownloadFileOperation>()
private var mCurrentDownload: DownloadFileOperation? = null
private var notificationManager: NotificationManager? = null
private var notification: Notification? = null
private var notificationBuilder: NotificationCompat.Builder? = null
private var mLastPercent = 0
private var conflictUploadId: Long = 0
var mStartedDownload = false
@JvmField
@Inject
var accountManager: UserAccountManager? = null
@JvmField
@Inject
var uploadsStorageManager: UploadsStorageManager? = null
@JvmField
@Inject
var localBroadcastManager: LocalBroadcastManager? = null
@JvmField
@Inject
var viewThemeUtils: ViewThemeUtils? = null
/**
* Service initialization
*/
override fun onCreate() {
super.onCreate()
AndroidInjection.inject(this)
Log_OC.d(TAG, "Creating service")
initNotificationManager()
val thread = HandlerThread("FileDownloaderThread", Process.THREAD_PRIORITY_BACKGROUND)
thread.start()
mServiceLooper = thread.looper
mServiceHandler = ServiceHandler(mServiceLooper, this)
mBinder = FileDownloaderBinder()
initNotificationBuilder()
// add AccountsUpdatedListener
val am = AccountManager.get(applicationContext)
am.addOnAccountsUpdatedListener(this, null, false)
}
private fun initNotificationManager() {
if (notificationManager == null) {
notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
}
}
private fun initNotificationBuilder() {
val resources = applicationContext.resources
val title = resources.getString(R.string.foreground_service_download)
notificationBuilder = NotificationUtils.newNotificationBuilder(this, viewThemeUtils)
.setSmallIcon(R.drawable.notification_icon)
.setOngoing(true)
.setContentTitle(title)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationBuilder?.setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_DOWNLOAD)
}
notification = notificationBuilder?.build()
}
private fun notifyNotificationManager() {
notificationManager?.notify(R.string.downloader_download_in_progress_ticker, notificationBuilder?.build())
}
/**
* Service clean up
*/
override fun onDestroy() {
Log_OC.v(TAG, "Destroying service")
mBinder = null
mServiceHandler = null
mServiceLooper!!.quit()
mServiceLooper = null
notificationManager = null
notification = null
notificationBuilder = null
// remove AccountsUpdatedListener
val am = AccountManager.get(applicationContext)
am.removeOnAccountsUpdatedListener(this)
super.onDestroy()
}
/**
* Entry point to add one or several files to the queue of downloads.
*
* New downloads 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.
*/
@Suppress("LongParameterList")
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
Log_OC.d(TAG, "Starting command with id $startId")
startForeground(FOREGROUND_SERVICE_ID, notification)
if (!intent.hasExtra(EXTRA_USER) || !intent.hasExtra(EXTRA_FILE)) {
Log_OC.e(TAG, "Not enough information provided in intent")
return START_NOT_STICKY
}
val user = intent.getParcelableExtra<User>(EXTRA_USER)
val file = intent.getParcelableExtra<OCFile>(EXTRA_FILE)
val behaviour = intent.getStringExtra(OCFileListFragment.DOWNLOAD_BEHAVIOUR)
var downloadType: DownloadType? = DownloadType.DOWNLOAD
if (intent.hasExtra(DOWNLOAD_TYPE)) {
downloadType = intent.getSerializableExtra(DOWNLOAD_TYPE) as DownloadType?
}
val activityName = intent.getStringExtra(SendShareDialog.ACTIVITY_NAME)
val packageName = intent.getStringExtra(SendShareDialog.PACKAGE_NAME)
conflictUploadId = intent.getLongExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD_ID, -1)
val requestedDownloads = handleDownloadRequest(user, file, behaviour, downloadType, activityName, packageName)
if (requestedDownloads.isNotEmpty()) {
val msg = mServiceHandler?.obtainMessage()
msg?.arg1 = startId
msg?.obj = requestedDownloads
msg?.let {
mServiceHandler?.sendMessage(it)
}
}
return START_NOT_STICKY
}
@Suppress("LongParameterList")
private fun handleDownloadRequest(
user: User?,
file: OCFile?,
behaviour: String?,
downloadType: DownloadType?,
activityName: String?,
packageName: String?
): List<String> {
val requestedDownloads: MutableList<String> = ArrayList()
if (user == null || file == null) {
return requestedDownloads
}
try {
val newDownload = DownloadFileOperation(
user,
file,
behaviour,
activityName,
packageName,
baseContext,
downloadType
)
newDownload.addDatatransferProgressListener(this)
newDownload.addDatatransferProgressListener(mBinder as FileDownloaderBinder?)
val putResult = mPendingDownloads.putIfAbsent(user.accountName, file.remotePath, newDownload)
if (putResult != null) {
val downloadKey = putResult.first
requestedDownloads.add(downloadKey)
sendBroadcastNewDownload(newDownload, putResult.second)
}
} catch (e: IllegalArgumentException) {
Log_OC.e(TAG, "Not enough information provided in intent: " + e.message)
}
return requestedDownloads
}
/**
* Provides a binder object that clients can use to perform operations on the queue of downloads,
* excepting the addition of new files.
*
* Implemented to perform cancellation, pause and resume of existing downloads.
*/
override fun onBind(intent: Intent): IBinder? {
return mBinder
}
/**
* Called when ALL the bound clients were onbound.
*/
override fun onUnbind(intent: Intent): Boolean {
(mBinder as FileDownloaderBinder?)!!.clearListeners()
return false // not accepting rebinding (default behaviour)
}
override fun onAccountsUpdated(accounts: Array<Account>) {
// review the current download and cancel it if its account doesn't exist
if (mCurrentDownload != null && !accountManager!!.exists(mCurrentDownload!!.user.toPlatformAccount())) {
mCurrentDownload!!.cancel()
}
// The rest of downloads are cancelled when they try to start
}
/**
* Binder to let client components to perform operations on the queue of downloads.
*
*
* It provides by itself the available operations.
*/
inner class FileDownloaderBinder : Binder(), OnDatatransferProgressListener {
/**
* Map of listeners that will be reported about progress of downloads from a
* [FileDownloaderBinder]
* instance.
*/
private val mBoundListeners: MutableMap<Long, OnDatatransferProgressListener> = HashMap()
/**
* Cancels a pending or current download of a remote file.
*
* @param account ownCloud account where the remote file is stored.
* @param file A file in the queue of pending downloads
*/
@Suppress("ComplexMethod")
fun cancel(account: Account, file: OCFile) {
val removeResult = mPendingDownloads.remove(account.name, file.remotePath)
val download = removeResult.first
if (download != null) {
download.cancel()
} else {
mCurrentDownload?.takeIf {
it.remotePath.startsWith(file.remotePath) && account.name == currentUser?.get()?.accountName
}?.cancel()
}
}
/**
* Cancels all the downloads for an account
*/
fun cancel(accountName: String?) {
if (mCurrentDownload != null && mCurrentDownload!!.user.nameEquals(accountName)) {
mCurrentDownload!!.cancel()
}
// Cancel pending downloads
cancelPendingDownloads(accountName)
}
fun clearListeners() {
mBoundListeners.clear()
}
/**
* Returns True when the file described by 'file' in the ownCloud account 'account'
* is downloading or waiting to download.
*
* If 'file' is a directory, returns 'true' if any of its descendant files is downloading or
* waiting to download.
*
* @param user user where the remote file is stored.
* @param file A file that could be in the queue of downloads.
*/
fun isDownloading(user: User?, file: OCFile?): Boolean {
return user != null && file != null && mPendingDownloads.contains(user.accountName, file.remotePath)
}
/**
* Adds a listener interested in the progress of the download for a concrete file.
*
* @param listener Object to notify about progress of transfer.
* @param file [OCFile] of interest for listener.
*/
fun addDataTransferProgressListener(listener: OnDatatransferProgressListener?, file: OCFile?) {
if (file == null || listener == null) {
return
}
mBoundListeners[file.fileId] = listener
}
/**
* Removes a listener interested in the progress of the download for a concrete file.
*
* @param listener Object to notify about progress of transfer.
* @param file [OCFile] of interest for listener.
*/
fun removeDataTransferProgressListener(listener: OnDatatransferProgressListener?, file: OCFile?) {
if (file == null || listener == null) {
return
}
val fileId = file.fileId
if (mBoundListeners[fileId] === listener) {
mBoundListeners.remove(fileId)
}
}
override fun onTransferProgress(
progressRate: Long,
totalTransferredSoFar: Long,
totalToTransfer: Long,
fileName: String
) {
val boundListener = mBoundListeners[mCurrentDownload!!.file.fileId]
boundListener?.onTransferProgress(
progressRate,
totalTransferredSoFar,
totalToTransfer,
fileName
)
}
}
/**
* Download worker. Performs the pending downloads in the order they were requested.
*
* Created with the Looper of a new thread, started in [FileUploader.onCreate].
*/
private class ServiceHandler(looper: Looper?, service: FileDownloader?) : Handler(looper!!) {
// don't make it a final class, and don't remove the static ; lint will warn about a
// possible memory leak
var mService: FileDownloader
init {
requireNotNull(service) { "Received invalid NULL in parameter 'service'" }
mService = service
}
@Suppress("MagicNumber")
override fun handleMessage(msg: Message) {
val requestedDownloads = msg.obj as AbstractList<String>
if (msg.obj != null) {
val it: Iterator<String> = requestedDownloads.iterator()
while (it.hasNext()) {
val next = it.next()
mService.downloadFile(next)
}
}
mService.mStartedDownload = false
Handler(Looper.getMainLooper()).postDelayed({
if (!mService.mStartedDownload) {
mService.notificationManager!!.cancel(R.string.downloader_download_in_progress_ticker)
}
Log_OC.d(TAG, "Stopping after command with id " + msg.arg1)
mService.notificationManager!!.cancel(FOREGROUND_SERVICE_ID)
mService.stopForeground(true)
mService.stopSelf(msg.arg1)
}, 2000)
}
}
/**
* Core download method: requests a file to download and stores it.
*
* @param downloadKey Key to access the download to perform, contained in mPendingDownloads
*/
@Suppress("NestedBlockDepth", "TooGenericExceptionCaught")
private fun downloadFile(downloadKey: String) {
mStartedDownload = true
mCurrentDownload = mPendingDownloads[downloadKey]
if (mCurrentDownload != null) {
val isAccountExist = accountManager?.exists(mCurrentDownload!!.user.toPlatformAccount())
if (isAccountExist == true) {
notifyDownloadStart(mCurrentDownload!!)
var downloadResult: RemoteOperationResult<*>? = null
try {
// / prepare client object to send the request to the ownCloud server
val currentDownloadAccount = mCurrentDownload!!.user.toPlatformAccount()
val currentDownloadUser = accountManager!!.getUser(currentDownloadAccount.name)
if (currentUser != currentDownloadUser) {
currentUser = currentDownloadUser
mStorageManager = FileDataStorageManager(currentUser.get(), contentResolver)
} // else, reuse storage manager from previous operation
// always get client from client manager, to get fresh credentials in case
// of update
val ocAccount = currentDownloadUser.get().toOwnCloudAccount()
mDownloadClient = OwnCloudClientManagerFactory.getDefaultSingleton().getClientFor(ocAccount, this)
// / perform the download
downloadResult = mCurrentDownload!!.execute(mDownloadClient)
if (downloadResult.isSuccess && mCurrentDownload!!.downloadType === DownloadType.DOWNLOAD) {
saveDownloadedFile()
}
} catch (e: Exception) {
Log_OC.e(TAG, "Error downloading", e)
downloadResult = RemoteOperationResult<Any?>(e)
} finally {
val removeResult = mPendingDownloads.removePayload(
mCurrentDownload!!.user.accountName,
mCurrentDownload!!.remotePath
)
if (downloadResult == null) {
downloadResult = RemoteOperationResult<Any?>(RuntimeException("Error downloading…"))
}
// / notify result
notifyDownloadResult(mCurrentDownload!!, downloadResult)
sendBroadcastDownloadFinished(mCurrentDownload!!, downloadResult, removeResult.second)
}
} else {
cancelPendingDownloads(mCurrentDownload!!.user.accountName)
}
}
}
/**
* Updates the OC File after a successful download.
*
* TODO move to DownloadFileOperation
* unify with code from [DocumentsStorageProvider] and [DownloadTask].
*/
private fun saveDownloadedFile() {
var file = mStorageManager?.getFileById(mCurrentDownload!!.file.fileId)
if (file == null) {
// try to get file via path, needed for overwriting existing files on conflict dialog
file = mStorageManager?.getFileByDecryptedRemotePath(mCurrentDownload!!.file.remotePath)
}
if (file == null) {
Log_OC.e(this, "Could not save " + mCurrentDownload!!.file.remotePath)
return
}
val syncDate = System.currentTimeMillis()
file.lastSyncDateForProperties = syncDate
file.lastSyncDateForData = syncDate
file.isUpdateThumbnailNeeded = true
file.modificationTimestamp = mCurrentDownload!!.modificationTimestamp
file.modificationTimestampAtLastSyncForData = mCurrentDownload!!.modificationTimestamp
file.etag = mCurrentDownload!!.etag
file.mimeType = mCurrentDownload!!.mimeType
file.storagePath = mCurrentDownload!!.savePath
file.fileLength = File(mCurrentDownload!!.savePath).length()
file.remoteId = mCurrentDownload!!.file.remoteId
mStorageManager!!.saveFile(file)
if (MimeTypeUtil.isMedia(mCurrentDownload!!.mimeType)) {
FileDataStorageManager.triggerMediaScan(file.storagePath, file)
}
mStorageManager!!.saveConflict(file, null)
}
/**
* Creates a status notification to show the download progress
*
* @param download Download operation starting.
*/
@Suppress("MagicNumber")
private fun notifyDownloadStart(download: DownloadFileOperation) {
val fileName = download.file.getFileNameWithExtension(10)
val titlePrefix = getString(R.string.file_downloader_notification_title_prefix)
val title = titlePrefix + fileName
// / update status notification with a progress bar
mLastPercent = 0
notificationBuilder
?.setContentTitle(title)
?.setTicker(title)
?.setProgress(100, 0, download.size < 0)
// / includes a pending intent in the notification showing the details view of the file
val showDetailsIntent: Intent = if (PreviewImageFragment.canBePreviewed(download.file)) {
Intent(this, PreviewImageActivity::class.java)
} else {
Intent(this, FileDisplayActivity::class.java)
}
showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, download.file)
showDetailsIntent.putExtra(FileActivity.EXTRA_USER, download.user)
showDetailsIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
notificationBuilder?.setContentIntent(
PendingIntent.getActivity(
this,
System.currentTimeMillis().toInt(),
showDetailsIntent,
PendingIntent.FLAG_IMMUTABLE
)
)
initNotificationManager()
notifyNotificationManager()
}
/**
* Callback method to update the progress bar in the status notification.
*/
@Suppress("MagicNumber")
override fun onTransferProgress(
progressRate: Long,
totalTransferredSoFar: Long,
totalToTransfer: Long,
filePath: String
) {
val percent = (100.0 * totalTransferredSoFar.toDouble() / totalToTransfer.toDouble()).toInt()
if (percent != mLastPercent) {
notificationBuilder?.setProgress(100, percent, totalToTransfer < 0)
initNotificationManager()
notifyNotificationManager()
}
mLastPercent = percent
}
/**
* Updates the status notification with the result of a download operation.
*
* @param downloadResult Result of the download operation.
* @param download Finished download operation
*/
@SuppressFBWarnings("DMI")
@Suppress("MagicNumber")
private fun notifyDownloadResult(
download: DownloadFileOperation,
downloadResult: RemoteOperationResult<*>
) {
initNotificationManager()
if (!downloadResult.isCancelled) {
if (downloadResult.isSuccess) {
if (conflictUploadId > 0) {
uploadsStorageManager!!.removeUpload(conflictUploadId)
}
// Don't show notification except an error has occurred.
return
}
var tickerId = if (downloadResult.isSuccess) {
R.string.downloader_download_succeeded_ticker
} else {
R.string.downloader_download_failed_ticker
}
val needsToUpdateCredentials = ResultCode.UNAUTHORIZED == downloadResult.code
tickerId = if (needsToUpdateCredentials) {
R.string.downloader_download_failed_credentials_error
} else {
tickerId
}
notificationBuilder
?.setSmallIcon(R.drawable.notification_icon)
?.setTicker(getString(tickerId))
?.setAutoCancel(true)
?.setOngoing(false)
?.setProgress(0, 0, false)
if (needsToUpdateCredentials) {
configureUpdateCredentialsNotification(download.user)
} else {
// TODO put something smart in showDetailsIntent
val showDetailsIntent = Intent()
notificationBuilder?.setContentIntent(
PendingIntent.getActivity(
this,
System.currentTimeMillis().toInt(),
showDetailsIntent,
PendingIntent.FLAG_IMMUTABLE
)
)
}
notificationBuilder?.setContentText(
ErrorMessageAdapter.getErrorCauseMessage(
downloadResult,
download,
resources
)
)
if (notificationManager != null) {
notificationManager?.notify(SecureRandom().nextInt(), notificationBuilder?.build())
// Remove success notification
if (downloadResult.isSuccess) {
// Sleep 2 seconds, so show the notification before remove it
NotificationUtils.cancelWithDelay(
notificationManager,
R.string.downloader_download_succeeded_ticker,
2000
)
}
}
}
}
private fun configureUpdateCredentialsNotification(user: User) {
// let the user update credentials with one click
val updateAccountCredentials = Intent(this, AuthenticatorActivity::class.java)
updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, user.toPlatformAccount())
updateAccountCredentials.putExtra(
AuthenticatorActivity.EXTRA_ACTION,
AuthenticatorActivity.ACTION_UPDATE_EXPIRED_TOKEN
)
updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND)
notificationBuilder!!.setContentIntent(
PendingIntent.getActivity(
this,
System.currentTimeMillis().toInt(),
updateAccountCredentials,
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
)
)
}
/**
* Sends a broadcast when a download finishes in order to the interested activities can
* update their view
*
* @param download Finished download operation
* @param downloadResult Result of the download operation
* @param unlinkedFromRemotePath Path in the downloads tree where the download was unlinked from
*/
private fun sendBroadcastDownloadFinished(
download: DownloadFileOperation,
downloadResult: RemoteOperationResult<*>,
unlinkedFromRemotePath: String?
) {
val end = Intent(downloadFinishMessage)
end.putExtra(EXTRA_DOWNLOAD_RESULT, downloadResult.isSuccess)
end.putExtra(ACCOUNT_NAME, download.user.accountName)
end.putExtra(EXTRA_REMOTE_PATH, download.remotePath)
end.putExtra(OCFileListFragment.DOWNLOAD_BEHAVIOUR, download.behaviour)
end.putExtra(SendShareDialog.ACTIVITY_NAME, download.activityName)
end.putExtra(SendShareDialog.PACKAGE_NAME, download.packageName)
if (unlinkedFromRemotePath != null) {
end.putExtra(EXTRA_LINKED_TO_PATH, unlinkedFromRemotePath)
}
end.setPackage(packageName)
localBroadcastManager!!.sendBroadcast(end)
}
/**
* Sends a broadcast when a new download is added to the queue.
*
* @param download Added download operation
* @param linkedToRemotePath Path in the downloads tree where the download was linked to
*/
private fun sendBroadcastNewDownload(
download: DownloadFileOperation,
linkedToRemotePath: String
) {
val added = Intent(downloadAddedMessage)
added.putExtra(ACCOUNT_NAME, download.user.accountName)
added.putExtra(EXTRA_REMOTE_PATH, download.remotePath)
added.putExtra(EXTRA_LINKED_TO_PATH, linkedToRemotePath)
added.setPackage(packageName)
localBroadcastManager!!.sendBroadcast(added)
}
private fun cancelPendingDownloads(accountName: String?) {
mPendingDownloads.remove(accountName)
}
companion object {
const val EXTRA_USER = "USER"
const val EXTRA_FILE = "FILE"
private const val DOWNLOAD_ADDED_MESSAGE = "DOWNLOAD_ADDED"
private const val DOWNLOAD_FINISH_MESSAGE = "DOWNLOAD_FINISH"
const val EXTRA_DOWNLOAD_RESULT = "RESULT"
const val EXTRA_REMOTE_PATH = "REMOTE_PATH"
const val EXTRA_LINKED_TO_PATH = "LINKED_TO"
const val ACCOUNT_NAME = "ACCOUNT_NAME"
const val DOWNLOAD_TYPE = "DOWNLOAD_TYPE"
private const val FOREGROUND_SERVICE_ID = 412
private val TAG = FileDownloader::class.java.simpleName
@JvmStatic
val downloadAddedMessage: String
get() = FileDownloader::class.java.name + DOWNLOAD_ADDED_MESSAGE
@JvmStatic
val downloadFinishMessage: String
get() = FileDownloader::class.java.name + DOWNLOAD_FINISH_MESSAGE
}
}

View File

@ -693,7 +693,7 @@ public class FileDetailFragment extends FileFragment implements OnClickListener,
if (progressListener != null) {
if (containerActivity.getFileDownloaderBinder() != null) {
containerActivity.getFileDownloaderBinder().
addDataTransferProgressListener(progressListener, getFile());
addDatatransferProgressListener(progressListener, getFile());
}
if (containerActivity.getFileUploaderBinder() != null) {
containerActivity.getFileUploaderBinder().
@ -708,7 +708,7 @@ public class FileDetailFragment extends FileFragment implements OnClickListener,
if (progressListener != null) {
if (containerActivity.getFileDownloaderBinder() != null) {
containerActivity.getFileDownloaderBinder().
removeDataTransferProgressListener(progressListener, getFile());
removeDatatransferProgressListener(progressListener, getFile());
}
if (containerActivity.getFileUploaderBinder() != null) {
containerActivity.getFileUploaderBinder().

View File

@ -261,7 +261,7 @@ public class FileDownloadFragment extends FileFragment implements OnClickListene
public void listenForTransferProgress() {
if (mProgressListener != null && !mListening && containerActivity.getFileDownloaderBinder() != null) {
containerActivity.getFileDownloaderBinder().addDataTransferProgressListener(mProgressListener, getFile());
containerActivity.getFileDownloaderBinder().addDatatransferProgressListener(mProgressListener, getFile());
mListening = true;
setButtonsForTransferring();
}
@ -271,7 +271,7 @@ public class FileDownloadFragment extends FileFragment implements OnClickListene
public void leaveTransferProgress() {
if (mProgressListener != null && containerActivity.getFileDownloaderBinder() != null) {
containerActivity.getFileDownloaderBinder()
.removeDataTransferProgressListener(mProgressListener, getFile());
.removeDatatransferProgressListener(mProgressListener, getFile());
mListening = false;
}
}