Merge branch 'master' into develop
[pub/Android/ownCloud.git] / src / com / owncloud / android / files / services / FileDownloader.java
index c9ad961..cd7afa6 100644 (file)
@@ -1,6 +1,6 @@
 /* ownCloud Android client application
  *   Copyright (C) 2012 Bartek Przybylski
- *   Copyright (C) 2012-2013 ownCloud Inc.
+ *   Copyright (C) 2012-2015 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,
@@ -25,8 +25,6 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Vector;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
 
 import com.owncloud.android.R;
 import com.owncloud.android.authentication.AuthenticatorActivity;
@@ -64,17 +62,19 @@ import android.os.Looper;
 import android.os.Message;
 import android.os.Process;
 import android.support.v4.app.NotificationCompat;
+import android.util.Pair;
 
 public class FileDownloader extends Service implements OnDatatransferProgressListener {
     
     public static final String EXTRA_ACCOUNT = "ACCOUNT";
     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_FILE_PATH = "FILE_PATH";
     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";
     
     private static final String TAG = "FileDownloader";
@@ -83,35 +83,25 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
     private ServiceHandler mServiceHandler;
     private IBinder mBinder;
     private OwnCloudClient mDownloadClient = null;
-    private Account mLastAccount = null;
+    private Account mCurrentAccount = null;
     private FileDataStorageManager mStorageManager;
     
-    private ConcurrentMap<String, DownloadFileOperation> mPendingDownloads = new ConcurrentHashMap<String, DownloadFileOperation>();
+    private IndexedForest<DownloadFileOperation> mPendingDownloads = new IndexedForest<DownloadFileOperation>();
+
     private DownloadFileOperation mCurrentDownload = null;
     
     private NotificationManager mNotificationManager;
     private NotificationCompat.Builder mNotificationBuilder;
     private int mLastPercent;
-    
+
     
     public static String getDownloadAddedMessage() {
-        return FileDownloader.class.getName().toString() + DOWNLOAD_ADDED_MESSAGE;
+        return FileDownloader.class.getName() + DOWNLOAD_ADDED_MESSAGE;
     }
     
     public static String getDownloadFinishMessage() {
-        return FileDownloader.class.getName().toString() + DOWNLOAD_FINISH_MESSAGE;
-    }
-    
-    /**
-     * Builds a key for mPendingDownloads from the account and file to download
-     * 
-     * @param account   Account where the file to download is stored
-     * @param file      File to download
-     */
-    private String buildRemoteName(Account account, OCFile file) {
-        return account.name + file.getRemotePath();
+        return FileDownloader.class.getName() + DOWNLOAD_FINISH_MESSAGE;
     }
-
     
     /**
      * Service initialization
@@ -119,63 +109,102 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
     @Override
     public void onCreate() {
         super.onCreate();
+        Log_OC.d(TAG, "Creating service");
         mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
-        HandlerThread thread = new HandlerThread("FileDownloaderThread",
-                Process.THREAD_PRIORITY_BACKGROUND);
+        HandlerThread thread = new HandlerThread("FileDownloaderThread", Process.THREAD_PRIORITY_BACKGROUND);
         thread.start();
         mServiceLooper = thread.getLooper();
         mServiceHandler = new ServiceHandler(mServiceLooper, this);
         mBinder = new FileDownloaderBinder();
     }
 
+
+    /**
+     * Service clean up
+     */
+    @Override
+    public void onDestroy() {
+        Log_OC.v(TAG, "Destroying service" );
+        mBinder = null;
+        mServiceHandler = null;
+        mServiceLooper.quit();
+        mServiceLooper = null;
+        mNotificationManager = null;
+        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.
+     *
+     * 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);
+
         if (    !intent.hasExtra(EXTRA_ACCOUNT) ||
                 !intent.hasExtra(EXTRA_FILE)
-                /*!intent.hasExtra(EXTRA_FILE_PATH) ||
-                !intent.hasExtra(EXTRA_REMOTE_PATH)*/
            ) {
             Log_OC.e(TAG, "Not enough information provided in intent");
             return START_NOT_STICKY;
-        }
-        Account account = intent.getParcelableExtra(EXTRA_ACCOUNT);
-        OCFile file = intent.getParcelableExtra(EXTRA_FILE);
-        
-        AbstractList<String> requestedDownloads = new Vector<String>(); // dvelasco: now this always contains just one element, but that can change in a near future (download of multiple selection)
-        String downloadKey = buildRemoteName(account, file);
-        try {
-            DownloadFileOperation newDownload = new DownloadFileOperation(account, file); 
-            mPendingDownloads.putIfAbsent(downloadKey, newDownload);
-            newDownload.addDatatransferProgressListener(this);
-            newDownload.addDatatransferProgressListener((FileDownloaderBinder)mBinder);
-            requestedDownloads.add(downloadKey);
-            sendBroadcastNewDownload(newDownload);
-            
-        } 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);
+        } else {
+            final Account account = intent.getParcelableExtra(EXTRA_ACCOUNT);
+            final OCFile file = intent.getParcelableExtra(EXTRA_FILE);
+
+            /*Log_OC.v(
+                    "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
+                    "Received request to download file"
+            );*/
+
+                AbstractList<String> requestedDownloads = new Vector<String>();
+                try {
+                    DownloadFileOperation newDownload = new DownloadFileOperation(account, file);
+                    newDownload.addDatatransferProgressListener(this);
+                    newDownload.addDatatransferProgressListener((FileDownloaderBinder) mBinder);
+                    Pair<String, String> putResult = mPendingDownloads.putIfAbsent(
+                        account, file.getRemotePath(), newDownload
+                    );
+                    String downloadKey = putResult.first;
+                    requestedDownloads.add(downloadKey);
+                    /*Log_OC.v(
+                        "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
+                        "Download on " + file.getRemotePath() + " added to queue"
+                    );*/
+
+                    // Store file on db with state 'downloading'
+                    /*
+                    TODO - check if helps with UI responsiveness, letting only folders use FileDownloaderBinder to check
+                    FileDataStorageManager storageManager = new FileDataStorageManager(account, getContentResolver());
+                    file.setDownloading(true);
+                    storageManager.saveFile(file);
+                    */
+
+                    sendBroadcastNewDownload(newDownload, putResult.second);
+
+                } 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. 
-     * 
+     * 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
@@ -193,33 +222,49 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
         return false;   // not accepting rebinding (default behaviour)
     }
 
-    
+
     /**
      *  Binder to let client components to perform operations on the queue of downloads.
-     * 
+     *
      *  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 
+         * Map of listeners that will be reported about progress of downloads from a {@link FileDownloaderBinder}
+         * instance.
          */
-        private Map<String, OnDatatransferProgressListener> mBoundListeners = new HashMap<String, OnDatatransferProgressListener>();
-        
-        
+        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 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) {
-            DownloadFileOperation download = null;
-            synchronized (mPendingDownloads) {
-                download = mPendingDownloads.remove(buildRemoteName(account, file));
-            }
+            /*Log_OC.v(
+                    "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
+                    "Received request to cancel download of " + file.getRemotePath()
+            );
+            Log_OC.v(   "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
+                    "Removing download of " + file.getRemotePath());*/
+            Pair<DownloadFileOperation, String> removeResult = mPendingDownloads.remove(account, file.getRemotePath());
+            DownloadFileOperation download = removeResult.first;
             if (download != null) {
+                /*Log_OC.v(   "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
+                        "Canceling returned download of " + file.getRemotePath());*/
                 download.cancel();
+            } else {
+                if (mCurrentDownload != null && mCurrentAccount != null &&
+                        mCurrentDownload.getRemotePath().startsWith(file.getRemotePath()) &&
+                        account.name.equals(mCurrentAccount.name)) {
+                    /*Log_OC.v(   "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
+                            "Canceling current sync as descendant: " + mCurrentDownload.getRemotePath());*/
+                    mCurrentDownload.cancel();
+                }
             }
         }
         
@@ -230,29 +275,18 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
 
 
         /**
-         * Returns True when the file described by 'file' in the ownCloud account 'account' is downloading or waiting to download.
+         * 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 some of its descendant files 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 account       Owncloud account where the remote file is stored.
+         * @param account       ownCloud account where the remote file is stored.
          * @param file          A file that could be in the queue of downloads.
          */
         public boolean isDownloading(Account account, OCFile file) {
             if (account == null || file == null) return false;
-            String targetKey = buildRemoteName(account, file);
-            synchronized (mPendingDownloads) {
-                if (file.isFolder()) {
-                    // this can be slow if there are many downloads :(
-                    Iterator<String> it = mPendingDownloads.keySet().iterator();
-                    boolean found = false;
-                    while (it.hasNext() && !found) {
-                        found = it.next().startsWith(targetKey);
-                    }
-                    return found;
-                } else {
-                    return (mPendingDownloads.containsKey(targetKey));
-                }
-            }
+            return (mPendingDownloads.contains(account, file.getRemotePath()));
         }
 
         
@@ -261,12 +295,14 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
          * 
          * @param listener      Object to notify about progress of transfer.    
          * @param account       ownCloud account holding the file of interest.
-         * @param file          {@link OCfile} of interest for listener. 
+         * @param file          {@link OCFile} of interest for listener.
          */
-        public void addDatatransferProgressListener (OnDatatransferProgressListener listener, Account account, OCFile file) {
+        public void addDatatransferProgressListener (
+                OnDatatransferProgressListener listener, Account account, OCFile file
+        ) {
             if (account == null || file == null || listener == null) return;
-            String targetKey = buildRemoteName(account, file);
-            mBoundListeners.put(targetKey, listener);
+            //String targetKey = buildKey(account, file.getRemotePath());
+            mBoundListeners.put(file.getFileId(), listener);
         }
         
         
@@ -275,21 +311,24 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
          * 
          * @param listener      Object to notify about progress of transfer.    
          * @param account       ownCloud account holding the file of interest.
-         * @param file          {@link OCfile} of interest for listener. 
+         * @param file          {@link OCFile} of interest for listener.
          */
-        public void removeDatatransferProgressListener (OnDatatransferProgressListener listener, Account account, OCFile file) {
+        public void removeDatatransferProgressListener (
+                OnDatatransferProgressListener listener, Account account, OCFile file
+        ) {
             if (account == null || file == null || listener == null) return;
-            String targetKey = buildRemoteName(account, file);
-            if (mBoundListeners.get(targetKey) == listener) {
-                mBoundListeners.remove(targetKey);
+            //String targetKey = buildKey(account, file.getRemotePath());
+            Long fileId = file.getFileId();
+            if (mBoundListeners.get(fileId) == listener) {
+                mBoundListeners.remove(fileId);
             }
         }
 
         @Override
         public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer,
                 String fileName) {
-            String key = buildRemoteName(mCurrentDownload.getAccount(), mCurrentDownload.getFile());
-            OnDatatransferProgressListener boundListener = mBoundListeners.get(key);
+            //String key = buildKey(mCurrentDownload.getAccount(), mCurrentDownload.getFile().getRemotePath());
+            OnDatatransferProgressListener boundListener = mBoundListeners.get(mCurrentDownload.getFile().getFileId());
             if (boundListener != null) {
                 boundListener.onTransferProgress(progressRate, totalTransferredSoFar, totalToTransfer, fileName);
             }
@@ -320,9 +359,11 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
             if (msg.obj != null) {
                 Iterator<String> it = requestedDownloads.iterator();
                 while (it.hasNext()) {
-                    mService.downloadFile(it.next());
+                    String next = it.next();
+                    mService.downloadFile(next);
                 }
             }
+            Log_OC.d(TAG, "Stopping after command with id " + msg.arg1);
             mService.stopSelf(msg.arg1);
         }
     }
@@ -334,11 +375,11 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
      * @param downloadKey   Key to access the download to perform, contained in mPendingDownloads 
      */
     private void downloadFile(String downloadKey) {
-        
-        synchronized(mPendingDownloads) {
-            mCurrentDownload = mPendingDownloads.get(downloadKey);
-        }
-        
+
+        /*Log_OC.v(   "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
+                "Getting download of " + downloadKey);*/
+        mCurrentDownload = mPendingDownloads.get(downloadKey);
+
         if (mCurrentDownload != null) {
             
             notifyDownloadStart(mCurrentDownload);
@@ -346,39 +387,48 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
             RemoteOperationResult downloadResult = null;
             try {
                 /// prepare client object to send the request to the ownCloud server
-                if (mDownloadClient == null || !mLastAccount.equals(mCurrentDownload.getAccount())) {
-                    mLastAccount = mCurrentDownload.getAccount();
-                    mStorageManager = 
-                            new FileDataStorageManager(mLastAccount, getContentResolver());
-                    OwnCloudAccount ocAccount = new OwnCloudAccount(mLastAccount, this);
-                    mDownloadClient = OwnCloudClientManagerFactory.getDefaultSingleton().
-                            getClientFor(ocAccount, this);
-                }
+                if (mCurrentAccount == null || !mCurrentAccount.equals(mCurrentDownload.getAccount())) {
+                    mCurrentAccount = mCurrentDownload.getAccount();
+                    mStorageManager = new FileDataStorageManager(
+                            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);
+                mDownloadClient = OwnCloudClientManagerFactory.getDefaultSingleton().
+                        getClientFor(ocAccount, this);
+
 
                 /// perform the download
+                /*Log_OC.v(   "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
+                        "Executing download of " + mCurrentDownload.getRemotePath());*/
                 downloadResult = mCurrentDownload.execute(mDownloadClient);
                 if (downloadResult.isSuccess()) {
                     saveDownloadedFile();
                 }
             
             } catch (AccountsException e) {
-                Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
+                Log_OC.e(TAG, "Error while trying to get authorization for " + mCurrentAccount.name, e);
                 downloadResult = new RemoteOperationResult(e);
             } catch (IOException e) {
-                Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
+                Log_OC.e(TAG, "Error while trying to get authorization for " + mCurrentAccount.name, e);
                 downloadResult = new RemoteOperationResult(e);
                 
             } finally {
-                synchronized(mPendingDownloads) {
-                    mPendingDownloads.remove(downloadKey);
-                }
+                /*Log_OC.v(   "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
+                        "Removing payload " + mCurrentDownload.getRemotePath());*/
+
+                Pair<DownloadFileOperation, String> removeResult =
+                        mPendingDownloads.removePayload(mCurrentAccount, mCurrentDownload.getRemotePath());
+
+                /// notify result
+                notifyDownloadResult(mCurrentDownload, downloadResult);
+
+                sendBroadcastDownloadFinished(mCurrentDownload, downloadResult, removeResult.second);
             }
 
-            
-            /// notify result
-            notifyDownloadResult(mCurrentDownload, downloadResult);
-            
-            sendBroadcastDownloadFinished(mCurrentDownload, downloadResult);
         }
     }
 
@@ -403,6 +453,15 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
         mStorageManager.triggerMediaScan(file.getStoragePath());
     }
 
+    /**
+     * Update the OC File after a unsuccessful download
+     */
+    private void updateUnsuccessfulDownloadedFile() {
+        OCFile file = mStorageManager.getFileById(mCurrentDownload.getFile().getFileId());
+        file.setDownloading(false);
+        mStorageManager.saveFile(file);
+    }
+
 
     /**
      * Creates a status notification to show the download progress
@@ -448,7 +507,8 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
      * Callback method to update the progress bar in the status notification.
      */
     @Override
-    public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filePath) {
+    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);
@@ -492,7 +552,9 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
                 // let the user update credentials with one click
                 Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
                 updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, download.getAccount());
-                updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_EXPIRED_TOKEN);
+                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);
@@ -500,8 +562,6 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
                     .setContentIntent(PendingIntent.getActivity(
                         this, (int) System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT));
                 
-                mDownloadClient = null;   // grant that future retries on the same account will get the fresh credentials
-                
             } else {
                 // TODO put something smart in showDetailsIntent
                 Intent   showDetailsIntent = new Intent();
@@ -510,7 +570,9 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
                         this, (int) System.currentTimeMillis(), showDetailsIntent, 0));
             }
             
-            mNotificationBuilder.setContentText(ErrorMessageAdapter.getErrorCauseMessage(downloadResult, download, getResources()));
+            mNotificationBuilder.setContentText(
+                    ErrorMessageAdapter.getErrorCauseMessage(downloadResult, download, getResources())
+            );
             mNotificationManager.notify(tickerId, mNotificationBuilder.build());
             
             // Remove success notification
@@ -529,15 +591,22 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
     /**
      * 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 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) {
+    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.getAccount().name);
         end.putExtra(EXTRA_REMOTE_PATH, download.getRemotePath());
         end.putExtra(EXTRA_FILE_PATH, download.getSavePath());
+        if (unlinkedFromRemotePath != null) {
+            end.putExtra(EXTRA_LINKED_TO_PATH, unlinkedFromRemotePath);
+        }
         sendStickyBroadcast(end);
     }
     
@@ -545,13 +614,15 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis
     /**
      * Sends a broadcast when a new download is added to the queue.
      * 
-     * @param download          Added download operation
+     * @param download              Added download operation
+     * @param linkedToRemotePath    Path in the downloads tree where the download was linked to
      */
-    private void sendBroadcastNewDownload(DownloadFileOperation download) {
+    private void sendBroadcastNewDownload(DownloadFileOperation download, String linkedToRemotePath) {
         Intent added = new Intent(getDownloadAddedMessage());
         added.putExtra(ACCOUNT_NAME, download.getAccount().name);
         added.putExtra(EXTRA_REMOTE_PATH, download.getRemotePath());
         added.putExtra(EXTRA_FILE_PATH, download.getSavePath());
+        added.putExtra(EXTRA_LINKED_TO_PATH, linkedToRemotePath);
         sendStickyBroadcast(added);
     }