/** * Handles requests to: - (re)start watching (ACTION_START_OBSERVE) - add an {@link OCFile} to be * watched (ATION_ADD_OBSERVED_FILE) - stop observing an {@link OCFile} (ACTION_DEL_OBSERVED_FILE) */ @Override public int onStartCommand(Intent intent, int flags, int startId) { Log_OC.d(TAG, "Starting command " + intent); if (intent == null || ACTION_START_OBSERVE.equals(intent.getAction())) { // NULL occurs when system tries to restart the service after its // process was killed startObservation(); return Service.START_STICKY; } else if (ACTION_ADD_OBSERVED_FILE.equals(intent.getAction())) { OCFile file = (OCFile) intent.getParcelableExtra(ARG_FILE); Account account = (Account) intent.getParcelableExtra(ARG_ACCOUNT); addObservedFile(file, account); } else if (ACTION_DEL_OBSERVED_FILE.equals(intent.getAction())) { removeObservedFile( (OCFile) intent.getParcelableExtra(ARG_FILE), (Account) intent.getParcelableExtra(ARG_ACCOUNT)); } else { Log_OC.e(TAG, "Unknown action recieved; ignoring it: " + intent.getAction()); } return Service.START_STICKY; }
@Override protected RemoteOperationResult run(OwnCloudClient client) { RemoteOperationResult result = null; /// download will be performed to a temporal file, then moved to the final location File tmpFile = new File(getTmpPath()); /// perform the download try { tmpFile.getParentFile().mkdirs(); int status = downloadFile(client, tmpFile); result = new RemoteOperationResult( isSuccess(status), status, (mGet != null ? mGet.getResponseHeaders() : null)); Log_OC.i( TAG, "Download of " + mRemotePath + " to " + getTmpPath() + ": " + result.getLogMessage()); } catch (Exception e) { result = new RemoteOperationResult(e); Log_OC.e( TAG, "Download of " + mRemotePath + " to " + getTmpPath() + ": " + result.getLogMessage(), e); } return result; }
/** * Entry point to add a new operation to the queue of operations. * * <p>New operations 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); // WIP: for the moment, only SYNC_FOLDER is expected here; // the rest of the operations are requested through the Binder if (ACTION_SYNC_FOLDER.equals(intent.getAction())) { if (!intent.hasExtra(EXTRA_ACCOUNT) || !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); String remotePath = intent.getStringExtra(EXTRA_REMOTE_PATH); Pair<Account, String> itemSyncKey = new Pair<Account, String>(account, remotePath); Pair<Target, RemoteOperation> itemToQueue = newOperation(intent); if (itemToQueue != null) { mSyncFolderHandler.add( account, remotePath, (SynchronizeFolderOperation) itemToQueue.second); Message msg = mSyncFolderHandler.obtainMessage(); msg.arg1 = startId; msg.obj = itemSyncKey; mSyncFolderHandler.sendMessage(msg); } } else { Message msg = mOperationsHandler.obtainMessage(); msg.arg1 = startId; mOperationsHandler.sendMessage(msg); } return START_NOT_STICKY; }
public RemoteOperationResult(boolean success, String bodyResponse, int httpCode) { mSuccess = success; mHttpCode = httpCode; if (success) { mCode = ResultCode.OK; } else if (httpCode > 0) { switch (httpCode) { case HttpStatus.SC_BAD_REQUEST: InputStream is = new ByteArrayInputStream(bodyResponse.getBytes()); InvalidCharacterExceptionParser xmlParser = new InvalidCharacterExceptionParser(); try { if (xmlParser.parseXMLResponse(is)) mCode = ResultCode.INVALID_CHARACTER_DETECT_IN_SERVER; } catch (Exception e) { mCode = ResultCode.UNHANDLED_HTTP_CODE; Log_OC.e(TAG, "Exception reading exception from server", e); } break; default: mCode = ResultCode.UNHANDLED_HTTP_CODE; Log_OC.d(TAG, "RemoteOperationResult has processed UNHANDLED_HTTP_CODE: " + httpCode); } } }
@Override public void run(AccountManagerFuture<Bundle> future) { FileActivity.this.mRedirectingToSetupAccount = false; boolean accountWasSet = false; if (future != null) { try { Bundle result; result = future.getResult(); String name = result.getString(AccountManager.KEY_ACCOUNT_NAME); String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE); if (AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), name)) { setAccount(new Account(name, type), false); accountWasSet = true; } } catch (OperationCanceledException e) { Log_OC.d(TAG, "Account creation canceled"); } catch (Exception e) { Log_OC.e(TAG, "Account creation finished in exception: ", e); } } else { Log_OC.e(TAG, "Account creation callback with null bundle"); } if (!accountWasSet) { moveTaskToBack(true); } }
@Override protected void onNewIntent(Intent intent) { Log_OC.v(TAG, "onNewIntent() start"); Account current = AccountUtils.getCurrentOwnCloudAccount(this); if (current != null && mAccount != null && !mAccount.name.equals(current.name)) { mAccount = current; } Log_OC.v(TAG, "onNewIntent() stop"); }
/** * Since ownCloud {@link Account}s can be managed from the system setting menu, the existence of * the {@link Account} associated to the instance must be checked every time it is restarted. */ @Override protected void onRestart() { Log_OC.v(TAG, "onRestart() start"); super.onRestart(); boolean validAccount = (mAccount != null && AccountUtils.exists(mAccount, this)); if (!validAccount) { swapToDefaultAccount(); } Log_OC.v(TAG, "onRestart() end"); }
/** {@inheritDoc} */ @Override public void onActivityCreated(Bundle savedInstanceState) { Log_OC.i(TAG, "onActivityCreated() start"); super.onActivityCreated(savedInstanceState); mAdapter = new LocalFileListAdapter(mContainerActivity.getInitialDirectory(), getActivity()); setListAdapter(mAdapter); Log_OC.i(TAG, "onActivityCreated() stop"); }
/** {@inheritDoc} */ @Override public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Log_OC.i(TAG, "onCreateView() start"); View v = super.onCreateView(inflater, container, savedInstanceState); setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); setSwipeEnabled(false); // Disable pull-to-refresh setFabEnabled(false); // Disable FAB setMessageForEmptyList(getString(R.string.local_file_list_empty)); Log_OC.i(TAG, "onCreateView() end"); return v; }
@Override protected RemoteOperationResult run(OwnCloudClient client) { RemoteOperationResult result = null; int status = -1; GetMethod get = null; try { // Get Method get = new GetMethod(client.getBaseUri() + ShareUtils.SHARING_API_PATH); // Add Parameters to Get Method get.setQueryString( new NameValuePair[] { new NameValuePair(PARAM_PATH, mRemoteFilePath), new NameValuePair(PARAM_RESHARES, String.valueOf(mReshares)), new NameValuePair(PARAM_SUBFILES, String.valueOf(mSubfiles)) }); get.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE); status = client.executeMethod(get); if (isSuccess(status)) { String response = get.getResponseBodyAsString(); // Parse xml response and obtain the list of shares ShareToRemoteOperationResultParser parser = new ShareToRemoteOperationResultParser(new ShareXMLParser()); parser.setOwnCloudVersion(client.getOwnCloudVersion()); parser.setServerBaseUri(client.getBaseUri()); result = parser.parse(response); if (result.isSuccess()) { Log_OC.d(TAG, "Got " + result.getData().size() + " shares"); } } else { result = new RemoteOperationResult(false, status, get.getResponseHeaders()); } } catch (Exception e) { result = new RemoteOperationResult(e); Log_OC.e(TAG, "Exception while getting shares", e); } finally { if (get != null) { get.releaseConnection(); } } return result; }
private void logCookiesAtRequest(Header[] headers, String when) { int counter = 0; for (int i = 0; i < headers.length; i++) { if (headers[i].getName().toLowerCase().equals("cookie")) { Log_OC.d( TAG + " #" + mInstanceNumber, "Cookies at request (" + when + ") (" + counter++ + "): " + headers[i].getValue()); } } if (counter == 0) { Log_OC.d(TAG + " #" + mInstanceNumber, "No cookie at request before"); } }
private void logSetCookiesAtResponse(Header[] headers) { int counter = 0; for (int i = 0; i < headers.length; i++) { if (headers[i].getName().toLowerCase().equals("set-cookie")) { Log_OC.d( TAG + " #" + mInstanceNumber, "Set-Cookie (" + counter++ + "): " + headers[i].getValue()); } } if (counter == 0) { Log_OC.d(TAG + " #" + mInstanceNumber, "No set-cookie"); } }
/** * Registers a local file to be observed for changes. * * @param localPath Absolute path in the local file system to the file to be observed. * @param account OwnCloud account associated to the local file. */ private void addObservedFile(String localPath, Account account) { File file = new File(localPath); String parentPath = file.getParent(); FolderObserver observer = mFolderObserversMap.get(parentPath); if (observer == null) { observer = new FolderObserver(parentPath, account, getApplicationContext()); mFolderObserversMap.put(parentPath, observer); Log_OC.d(TAG, "Observer added for parent folder " + parentPath + "/"); } observer.startWatching(file.getName()); Log_OC.d(TAG, "Added " + localPath + " to list of observed children"); }
/** * Unregisters a local file from being observed for changes. * * @param localPath Absolute path in the local file system to the target file. */ private void removeObservedFile(String localPath) { File file = new File(localPath); String parentPath = file.getParent(); FolderObserver observer = mFolderObserversMap.get(parentPath); if (observer != null) { observer.stopWatching(file.getName()); if (observer.isEmpty()) { mFolderObserversMap.remove(parentPath); Log_OC.d(TAG, "Observer removed for parent folder " + parentPath + "/"); } } else { Log_OC.d(TAG, "No observer to remove for path " + localPath); } }
/** * Lists the given directory on the view. When the input parameter is null, it will either refresh * the last known directory. list the root if there never was a directory. * * @param directory Directory to be listed */ public void listDirectory(File directory) { // Check input parameters for null if (directory == null) { if (mDirectory != null) { directory = mDirectory; } else { directory = Environment.getExternalStorageDirectory(); // TODO be careful with the state of the storage; could not be available if (directory == null) return; // no files to show } } // if that's not a directory -> List its parent if (!directory.isDirectory()) { Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString()); directory = directory.getParentFile(); } // by now, only files in the same directory will be kept as selected ((ListView) mCurrentListView).clearChoices(); mAdapter.swapDirectory(directory); if (mDirectory == null || !mDirectory.equals(directory)) { mCurrentListView.setSelection(0); } mDirectory = directory; }
@Override public void onDestroy() { Log_OC.v(TAG, "Destroying service"); // Saving cookies try { OwnCloudClientManagerFactory.getDefaultSingleton() .saveAllClients(this, MainApp.getAccountType()); // TODO - get rid of these exceptions } catch (AccountNotFoundException e) { e.printStackTrace(); } catch (AuthenticatorException e) { e.printStackTrace(); } catch (OperationCanceledException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } mUndispatchedFinishedOperations.clear(); mOperationsBinder = null; mOperationsHandler.getLooper().quit(); mOperationsHandler = null; mSyncFolderHandler.getLooper().quit(); mSyncFolderHandler = null; super.onDestroy(); }
/** * Checks the file clicked over. Browses inside if it is a directory. Notifies the container * activity in any case. */ @Override public void onItemClick(AdapterView<?> l, View v, int position, long id) { File file = (File) mAdapter.getItem(position); if (file != null) { /// Click on a directory if (file.isDirectory()) { // just local updates listDirectory(file); // notify the click to container Activity mContainerActivity.onDirectoryClick(file); // save index and top position saveIndexAndTopPosition(position); } else { /// Click on a file ImageView checkBoxV = (ImageView) v.findViewById(R.id.custom_checkbox); if (checkBoxV != null) { if (((ListView) getListView()).isItemChecked(position)) { checkBoxV.setImageResource(R.drawable.ic_checkbox_marked); } else { checkBoxV.setImageResource(R.drawable.ic_checkbox_blank_outline); } } // notify the change to the container Activity mContainerActivity.onFileClick(file); } } else { Log_OC.w(TAG, "Null object in ListAdapter!!"); } }
/** * Notifies the currently subscribed listeners about the end of an operation. * * @param operation Finished operation. * @param result Result of the operation. */ protected void dispatchResultToOperationListeners( final RemoteOperation operation, final RemoteOperationResult result) { int count = 0; Iterator<OnRemoteOperationListener> listeners = mOperationsBinder.mBoundListeners.keySet().iterator(); while (listeners.hasNext()) { final OnRemoteOperationListener listener = listeners.next(); final Handler handler = mOperationsBinder.mBoundListeners.get(listener); if (handler != null) { handler.post( new Runnable() { @Override public void run() { listener.onRemoteOperationFinish(operation, result); } }); count += 1; } } if (count == 0) { Pair<RemoteOperation, RemoteOperationResult> undispatched = new Pair<RemoteOperation, RemoteOperationResult>(operation, result); mUndispatchedFinishedOperations.put(((Runnable) operation).hashCode(), undispatched); } Log_OC.d(TAG, "Called " + count + " listeners"); }
private RemoteOperationResult(boolean success, int httpCode) { mSuccess = success; mHttpCode = httpCode; if (success) { mCode = ResultCode.OK; } else if (httpCode > 0) { switch (httpCode) { case HttpStatus.SC_UNAUTHORIZED: mCode = ResultCode.UNAUTHORIZED; break; case HttpStatus.SC_NOT_FOUND: mCode = ResultCode.FILE_NOT_FOUND; break; case HttpStatus.SC_INTERNAL_SERVER_ERROR: mCode = ResultCode.INSTANCE_NOT_CONFIGURED; break; case HttpStatus.SC_CONFLICT: mCode = ResultCode.CONFLICT; break; case HttpStatus.SC_INSUFFICIENT_STORAGE: mCode = ResultCode.QUOTA_EXCEEDED; break; case HttpStatus.SC_FORBIDDEN: mCode = ResultCode.FORBIDDEN; break; default: mCode = ResultCode.UNHANDLED_HTTP_CODE; Log_OC.d(TAG, "RemoteOperationResult has processed UNHANDLED_HTTP_CODE: " + httpCode); } } }
public void shareFileWithLink(OCFile file) { if (isSharedSupported()) { if (file != null) { String link = "https://fake.url"; Intent intent = createShareWithLinkIntent(link); String[] packagesToExclude = new String[] {mFileActivity.getPackageName()}; DialogFragment chooserDialog = ShareLinkToDialog.newInstance(intent, packagesToExclude, file); chooserDialog.show(mFileActivity.getSupportFragmentManager(), FTAG_CHOOSER_DIALOG); } else { Log_OC.wtf(TAG, "Trying to share a NULL OCFile"); } } else { // Show a Message Toast t = Toast.makeText( mFileActivity, mFileActivity.getString(R.string.share_link_no_support_share_api), Toast.LENGTH_LONG); t.show(); } }
@Override public void onServiceDisconnected(ComponentName component) { if (component.equals(new ComponentName(FileActivity.this, OperationsService.class))) { Log_OC.d(TAG, "Operations service disconnected"); mOperationsServiceBinder = null; // TODO whatever could be waiting for the service is unbound } }
@Override protected int uploadFile(OwnCloudClient client) throws HttpException, IOException { int status = -1; FileChannel channel = null; RandomAccessFile raf = null; try { File file = new File(mLocalPath); raf = new RandomAccessFile(file, "r"); channel = raf.getChannel(); mEntity = new ChunkFromFileChannelRequestEntity(channel, mMimeType, CHUNK_SIZE, file); // ((ProgressiveDataTransferer)mEntity).addDatatransferProgressListeners(getDataTransferListeners()); synchronized (mDataTransferListeners) { ((ProgressiveDataTransferer) mEntity) .addDatatransferProgressListeners(mDataTransferListeners); } long offset = 0; String uriPrefix = client.getWebdavUri() + WebdavUtils.encodePath(mRemotePath) + "-chunking-" + Math.abs((new Random()).nextInt(9000) + 1000) + "-"; long chunkCount = (long) Math.ceil((double) file.length() / CHUNK_SIZE); for (int chunkIndex = 0; chunkIndex < chunkCount; chunkIndex++, offset += CHUNK_SIZE) { if (mPutMethod != null) { mPutMethod.releaseConnection(); // let the connection available for other methods } mPutMethod = new PutMethod(uriPrefix + chunkCount + "-" + chunkIndex); mPutMethod.addRequestHeader(OC_CHUNKED_HEADER, OC_CHUNKED_HEADER); ((ChunkFromFileChannelRequestEntity) mEntity).setOffset(offset); mPutMethod.setRequestEntity(mEntity); status = client.executeMethod(mPutMethod); client.exhaustResponse(mPutMethod.getResponseBodyAsStream()); Log_OC.d( TAG, "Upload of " + mLocalPath + " to " + mRemotePath + ", chunk index " + chunkIndex + ", count " + chunkCount + ", HTTP result status " + status); if (!isSuccess(status)) break; } } finally { if (channel != null) channel.close(); if (raf != null) raf.close(); if (mPutMethod != null) mPutMethod.releaseConnection(); // let the connection available for other methods } return status; }
/** * Called when the ownCloud {@link Account} associated to the Activity was just updated. * * <p>Child classes must grant that state depending on the {@link Account} is updated. */ protected void onAccountSet(boolean stateWasRecovered) { if (getAccount() != null) { mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver()); mCapabilities = mStorageManager.getCapability(mAccount.name); } else { Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!"); } }
private int patchRedirection(int status, HttpMethod method) throws HttpException, IOException { int redirectionsCount = 0; while (redirectionsCount < MAX_REDIRECTIONS_COUNT && (status == HttpStatus.SC_MOVED_PERMANENTLY || status == HttpStatus.SC_MOVED_TEMPORARILY || status == HttpStatus.SC_TEMPORARY_REDIRECT)) { Header location = method.getResponseHeader("Location"); if (location == null) { location = method.getResponseHeader("location"); } if (location != null) { Log_OC.d(TAG + " #" + mInstanceNumber, "Location to redirect: " + location.getValue()); method.setURI(new URI(location.getValue(), true)); Header destination = method.getRequestHeader("Destination"); if (destination == null) { destination = method.getRequestHeader("destination"); } if (destination != null) { String locationStr = location.getValue(); int suffixIndex = locationStr.lastIndexOf( (mCredentials instanceof OwnCloudBearerCredentials) ? AccountUtils.ODAV_PATH : AccountUtils.WEBDAV_PATH_4_0); String redirectionBase = locationStr.substring(0, suffixIndex); String destinationStr = destination.getValue(); String destinationPath = destinationStr.substring(mBaseUri.toString().length()); String redirectedDestination = redirectionBase + destinationPath; destination.setValue(redirectedDestination); method.setRequestHeader(destination); } status = super.executeMethod(method); redirectionsCount++; } else { Log_OC.d(TAG + " #" + mInstanceNumber, "No location to redirect!"); status = HttpStatus.SC_NOT_FOUND; } } return status; }
/** * Unregisters the local copy of a remote file to be observed for local changes. * * @param file Object representing a remote file which local copy must be not observed longer. * @param account OwnCloud account containing file. */ private void removeObservedFile(OCFile file, Account account) { Log_OC.v(TAG, "Removing a file from being watched"); if (file == null) { Log_OC.e(TAG, "Trying to remove a NULL file"); return; } if (account == null) { Log_OC.e(TAG, "Trying to add a file with a NULL account to observer"); return; } String localPath = file.getStoragePath(); if (localPath == null || localPath.length() <= 0) { localPath = FileStorageUtils.getDefaultSavePathFor(account.name, file); } removeObservedFile(localPath); }
/** * Registers the local copy of a remote file to be observed for local changes, an automatically * updated in the ownCloud server. * * <p>This method does NOT perform a {@link SynchronizeFileOperation} over the file. * * @param file Object representing a remote file which local copy must be observed. * @param account OwnCloud account containing file. */ private void addObservedFile(OCFile file, Account account) { Log_OC.v(TAG, "Adding a file to be watched"); if (file == null) { Log_OC.e(TAG, "Trying to add a NULL file to observer"); return; } if (account == null) { Log_OC.e(TAG, "Trying to add a file with a NULL account to observer"); return; } String localPath = file.getStoragePath(); if (localPath == null || localPath.length() <= 0) { // file downloading or to be downloaded for the first time localPath = FileStorageUtils.getDefaultSavePathFor(account.name, file); } addObservedFile(localPath, account); }
private void logCookie(Cookie cookie) { Log_OC.d(TAG, "Cookie name: " + cookie.getName()); Log_OC.d(TAG, " value: " + cookie.getValue()); Log_OC.d(TAG, " domain: " + cookie.getDomain()); Log_OC.d(TAG, " path: " + cookie.getPath()); Log_OC.d(TAG, " version: " + cookie.getVersion()); Log_OC.d( TAG, " expiryDate: " + (cookie.getExpiryDate() != null ? cookie.getExpiryDate().toString() : "--")); Log_OC.d(TAG, " comment: " + cookie.getComment()); Log_OC.d(TAG, " secure: " + cookie.getSecure()); }
/** * Attempts to get a new socket connection to the given host within the given time limit. * * @param host the host name/IP * @param port the port on the host * @param clientHost the local host name/IP to bind the socket to * @param clientPort the port on the local machine * @param params {@link HttpConnectionParams Http connection parameters} * @return Socket a new socket * @throws IOException if an I/O error occurs while creating the socket * @throws UnknownHostException if the IP address of the host cannot be determined */ public Socket createSocket( final String host, final int port, final InetAddress localAddress, final int localPort, final HttpConnectionParams params) throws IOException, UnknownHostException, ConnectTimeoutException { Log_OC.d( TAG, "Creating SSL Socket with remote " + host + ":" + port + ", local " + localAddress + ":" + localPort + ", params: " + params); if (params == null) { throw new IllegalArgumentException("Parameters may not be null"); } int timeout = params.getConnectionTimeout(); // logSslInfo(); SocketFactory socketfactory = mSslContext.getSocketFactory(); Log_OC.d( TAG, " ... with connection timeout " + timeout + " and socket timeout " + params.getSoTimeout()); Socket socket = socketfactory.createSocket(); SocketAddress localaddr = new InetSocketAddress(localAddress, localPort); SocketAddress remoteaddr = new InetSocketAddress(host, port); socket.setSoTimeout(params.getSoTimeout()); socket.bind(localaddr); ServerNameIndicator.setServerNameIndication(host, (SSLSocket) socket); socket.connect(remoteaddr, timeout); verifyPeerIdentity(host, port, socket); return socket; }
@Override public int executeMethod(HttpMethod method) throws IOException, HttpException { try { // just to log boolean customRedirectionNeeded = false; try { method.setFollowRedirects(mFollowRedirects); } catch (Exception e) { /* if (mFollowRedirects) Log_OC.d(TAG, "setFollowRedirects failed for " + method.getName() + " method, custom redirection will be used if needed"); */ customRedirectionNeeded = mFollowRedirects; } Log_OC.d( TAG + " #" + mInstanceNumber, "REQUEST " + method.getName() + " " + method.getPath()); // logCookiesAtRequest(method.getRequestHeaders(), "before"); // logCookiesAtState("before"); int status = super.executeMethod(method); if (customRedirectionNeeded) { status = patchRedirection(status, method); } // logCookiesAtRequest(method.getRequestHeaders(), "after"); // logCookiesAtState("after"); // logSetCookiesAtResponse(method.getResponseHeaders()); return status; } catch (IOException e) { Log_OC.d(TAG + " #" + mInstanceNumber, "Exception occured", e); throw e; } }
/** Initialize the service. */ @Override public void onCreate() { Log_OC.d(TAG, "onCreate"); super.onCreate(); mDownloadReceiver = new DownloadCompletedReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction(FileDownloader.getDownloadAddedMessage()); filter.addAction(FileDownloader.getDownloadFinishMessage()); registerReceiver(mDownloadReceiver, filter); mFolderObserversMap = new HashMap<String, FolderObserver>(); }