From: David A. Velasco Date: Mon, 15 Apr 2013 11:12:04 +0000 (+0200) Subject: Merge branch 'develop' into loggingtool X-Git-Tag: oc-android-1.4.3~36^2~4 X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/commitdiff_plain/c3ca5b5a4fda44999f215a3559921a4781f310f8?ds=inline;hp=--cc Merge branch 'develop' into loggingtool Conflicts: AndroidManifest.xml res/menu/main_menu.xml src/com/owncloud/android/datamodel/OCFile.java src/com/owncloud/android/files/services/FileDownloader.java src/com/owncloud/android/files/services/InstantUploadService.java src/com/owncloud/android/operations/SynchronizeFileOperation.java src/com/owncloud/android/operations/UploadFileOperation.java src/com/owncloud/android/ui/activity/FileDetailActivity.java src/com/owncloud/android/ui/activity/FileDisplayActivity.java src/com/owncloud/android/ui/activity/UploadFilesActivity.java src/com/owncloud/android/ui/fragment/FileDetailFragment.java src/com/owncloud/android/ui/fragment/OCFileListFragment.java src/eu/alefzero/webdav/ChunkFromFileChannelRequestEntity.java src/eu/alefzero/webdav/FileRequestEntity.java src/eu/alefzero/webdav/WebdavClient.java --- c3ca5b5a4fda44999f215a3559921a4781f310f8 diff --cc AndroidManifest.xml index a6f4b5bc,e5e4a059..4570ea14 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@@ -1,167 -1,174 +1,176 @@@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + ++ android:versionCode="104000" ++ android:versionName="1.4.0" xmlns:android="http://schemas.android.com/apk/res/android"> + + + + + + + + + + + + + + - ++ ++ + + + + + + + + + + + + + + + + + ++ + + + + + + + + + + + + + + + + + + + + + + + + + + ++ ++ ++ ++ ++ + + + + + + + + + + + + + + + + + + + + + + + + + + - - - ++ ++ ++ ++ + + + + + + + + ++ + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - ++ + diff --cc src/com/owncloud/android/datamodel/OCFile.java index 5631854d,8b079961..da105d93 --- a/src/com/owncloud/android/datamodel/OCFile.java +++ b/src/com/owncloud/android/datamodel/OCFile.java @@@ -21,11 -21,12 +21,11 @@@ package com.owncloud.android.datamodel import java.io.File; -import android.content.Intent; -import android.net.Uri; +import com.owncloud.android.Log_OC; + import android.os.Parcel; import android.os.Parcelable; --import android.util.Log; + import android.webkit.MimeTypeMap; public class OCFile implements Parcelable, Comparable { diff --cc src/com/owncloud/android/db/DbHandler.java index fb4e5399,4d212fed..788fec9f --- a/src/com/owncloud/android/db/DbHandler.java +++ b/src/com/owncloud/android/db/DbHandler.java @@@ -57,16 -55,18 +57,18 @@@ public class DbHandler cv.put("path", filepath); cv.put("account", account); cv.put("attempt", UPLOAD_STATUS_UPLOAD_LATER); + cv.put("message", message); long result = mDB.insert(TABLE_INSTANT_UPLOAD, null, cv); - Log.d(TABLE_INSTANT_UPLOAD, "putFileForLater returns with: " + result + " for file: " + filepath); + Log_OC.d(TABLE_INSTANT_UPLOAD, "putFileForLater returns with: " + result + " for file: " + filepath); return result != -1; } - public int updateFileState(String filepath, Integer status) { + public int updateFileState(String filepath, Integer status, String message) { ContentValues cv = new ContentValues(); cv.put("attempt", status); + cv.put("message", message); int result = mDB.update(TABLE_INSTANT_UPLOAD, cv, "path=?", new String[] { filepath }); - Log.d(TABLE_INSTANT_UPLOAD, "updateFileState returns with: " + result + " for file: " + filepath); + Log_OC.d(TABLE_INSTANT_UPLOAD, "updateFileState returns with: " + result + " for file: " + filepath); return result; } diff --cc src/com/owncloud/android/files/services/FileDownloader.java index a9aa2dc1,aa5583d0..39b3e953 --- a/src/com/owncloud/android/files/services/FileDownloader.java +++ b/src/com/owncloud/android/files/services/FileDownloader.java @@@ -1,417 -1,511 +1,510 @@@ -/* ownCloud Android client application - * Copyright (C) 2012 Bartek Przybylski - * Copyright (C) 2012-2013 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -package com.owncloud.android.files.services; - -import java.io.File; -import java.util.AbstractList; -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.datamodel.FileDataStorageManager; -import com.owncloud.android.datamodel.OCFile; -import eu.alefzero.webdav.OnDatatransferProgressListener; - -import com.owncloud.android.network.OwnCloudClientUtils; -import com.owncloud.android.operations.DownloadFileOperation; -import com.owncloud.android.operations.RemoteOperationResult; -import com.owncloud.android.ui.activity.FileDetailActivity; -import com.owncloud.android.ui.fragment.FileDetailFragment; -import com.owncloud.android.ui.preview.PreviewImageActivity; -import com.owncloud.android.ui.preview.PreviewImageFragment; - -import android.accounts.Account; -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.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.os.Process; -import android.util.Log; -import android.widget.RemoteViews; - -import com.owncloud.android.AccountUtils; -import com.owncloud.android.R; -import eu.alefzero.webdav.WebdavClient; - -public class FileDownloader extends Service implements OnDatatransferProgressListener { - - public static final String EXTRA_ACCOUNT = "ACCOUNT"; - public static final String EXTRA_FILE = "FILE"; - - public static final String DOWNLOAD_ADDED_MESSAGE = "DOWNLOAD_ADDED"; - public 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 ACCOUNT_NAME = "ACCOUNT_NAME"; - - private static final String TAG = "FileDownloader"; - - private Looper mServiceLooper; - private ServiceHandler mServiceHandler; - private IBinder mBinder; - private WebdavClient mDownloadClient = null; - private Account mLastAccount = null; - private FileDataStorageManager mStorageManager; - - private ConcurrentMap mPendingDownloads = new ConcurrentHashMap(); - private DownloadFileOperation mCurrentDownload = null; - - private NotificationManager mNotificationManager; - private Notification mNotification; - private int mLastPercent; - - - /** - * 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(); - } - - - /** - * Service initialization - */ - @Override - public void onCreate() { - super.onCreate(); - 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(); - } - - - /** - * 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) { - if ( !intent.hasExtra(EXTRA_ACCOUNT) || - !intent.hasExtra(EXTRA_FILE) - /*!intent.hasExtra(EXTRA_FILE_PATH) || - !intent.hasExtra(EXTRA_REMOTE_PATH)*/ - ) { - Log.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 requestedDownloads = new Vector(); // 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.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 arg0) { - 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) - } - - - /** - * 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 - */ - private Map mBoundListeners = new 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 - */ - public void cancel(Account account, OCFile file) { - DownloadFileOperation download = null; - synchronized (mPendingDownloads) { - download = mPendingDownloads.remove(buildRemoteName(account, file)); - } - if (download != null) { - download.cancel(); - } - } - - - 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 some of its descendant files is downloading or waiting to download. - * - * @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.isDirectory()) { - // this can be slow if there are many downloads :( - Iterator it = mPendingDownloads.keySet().iterator(); - boolean found = false; - while (it.hasNext() && !found) { - found = it.next().startsWith(targetKey); - } - return found; - } else { - return (mPendingDownloads.containsKey(targetKey)); - } - } - } - - - /** - * Adds a listener interested in the progress of the download for a concrete file. - * - * @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. - */ - 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); - } - - - - /** - * Removes a listener interested in the progress of the download for a concrete file. - * - * @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. - */ - 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); - } - } - - - @Override - public void onTransferProgress(long progressRate) { - // old way, should not be in use any more - } - - - @Override - public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, - String fileName) { - String key = buildRemoteName(mCurrentDownload.getAccount(), mCurrentDownload.getFile()); - OnDatatransferProgressListener boundListener = mBoundListeners.get(key); - 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 requestedDownloads = (AbstractList) msg.obj; - if (msg.obj != null) { - Iterator it = requestedDownloads.iterator(); - while (it.hasNext()) { - mService.downloadFile(it.next()); - } - } - mService.stopSelf(msg.arg1); - } - } - - - - /** - * 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) { - - synchronized(mPendingDownloads) { - mCurrentDownload = mPendingDownloads.get(downloadKey); - } - - if (mCurrentDownload != null) { - - notifyDownloadStart(mCurrentDownload); - - /// 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()); - mDownloadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext()); - } - - /// perform the download - RemoteOperationResult downloadResult = null; - try { - downloadResult = mCurrentDownload.execute(mDownloadClient); - if (downloadResult.isSuccess()) { - saveDownloadedFile(); - } - - } finally { - synchronized(mPendingDownloads) { - mPendingDownloads.remove(downloadKey); - } - } - - - /// notify result - notifyDownloadResult(mCurrentDownload, downloadResult); - - sendBroadcastDownloadFinished(mCurrentDownload, downloadResult); - } - } - - - /** - * Updates the OC File after a successful download. - */ - private void saveDownloadedFile() { - OCFile file = mCurrentDownload.getFile(); - long syncDate = System.currentTimeMillis(); - file.setLastSyncDateForProperties(syncDate); - file.setLastSyncDateForData(syncDate); - file.setModificationTimestamp(mCurrentDownload.getModificationTimestamp()); - file.setModificationTimestampAtLastSyncForData(mCurrentDownload.getModificationTimestamp()); - // file.setEtag(mCurrentDownload.getEtag()); // TODO Etag, where available - file.setMimetype(mCurrentDownload.getMimeType()); - file.setStoragePath(mCurrentDownload.getSavePath()); - file.setFileLength((new File(mCurrentDownload.getSavePath()).length())); - mStorageManager.saveFile(file); - } - - - /** - * 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; - mNotification = new Notification(R.drawable.icon, getString(R.string.downloader_download_in_progress_ticker), System.currentTimeMillis()); - mNotification.flags |= Notification.FLAG_ONGOING_EVENT; - mNotification.contentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.progressbar_layout); - mNotification.contentView.setProgressBar(R.id.status_progress, 100, 0, download.getSize() < 0); - mNotification.contentView.setTextViewText(R.id.status_text, String.format(getString(R.string.downloader_download_in_progress_content), 0, new File(download.getSavePath()).getName())); - mNotification.contentView.setImageViewResource(R.id.status_icon, R.drawable.icon); - - /// 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, FileDetailActivity.class); - } - showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, download.getFile()); - showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, download.getAccount()); - showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), showDetailsIntent, 0); - - mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotification); - } - - - /** - * Callback method to update the progress bar in the status notification. - */ - @Override - public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String fileName) { - int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer)); - if (percent != mLastPercent) { - mNotification.contentView.setProgressBar(R.id.status_progress, 100, percent, totalToTransfer < 0); - String text = String.format(getString(R.string.downloader_download_in_progress_content), percent, fileName); - mNotification.contentView.setTextViewText(R.id.status_text, text); - mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotification); - } - mLastPercent = percent; - } - - - /** - * Callback method to update the progress bar in the status notification (old version) - */ - @Override - public void onTransferProgress(long progressRate) { - // NOTHING TO DO HERE ANYMORE - } - - - /** - * Updates the status notification with the result of a download operation. - * - * @param downloadResult Result of the download operation. - * @param download Finished download operation - */ - private void notifyDownloadResult(DownloadFileOperation download, RemoteOperationResult downloadResult) { - mNotificationManager.cancel(R.string.downloader_download_in_progress_ticker); - if (!downloadResult.isCancelled()) { - int tickerId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_ticker : R.string.downloader_download_failed_ticker; - int contentId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_content : R.string.downloader_download_failed_content; - Notification finalNotification = new Notification(R.drawable.icon, getString(tickerId), System.currentTimeMillis()); - finalNotification.flags |= Notification.FLAG_AUTO_CANCEL; - Intent showDetailsIntent = null; - if (downloadResult.isSuccess()) { - if (PreviewImageFragment.canBePreviewed(download.getFile())) { - showDetailsIntent = new Intent(this, PreviewImageActivity.class); - } else { - showDetailsIntent = new Intent(this, FileDetailActivity.class); - } - showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, download.getFile()); - showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, download.getAccount()); - showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - - } else { - // TODO put something smart in showDetailsIntent - showDetailsIntent = new Intent(); - } - finalNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), showDetailsIntent, 0); - finalNotification.setLatestEventInfo(getApplicationContext(), getString(tickerId), String.format(getString(contentId), new File(download.getSavePath()).getName()), finalNotification.contentIntent); - mNotificationManager.notify(tickerId, finalNotification); - } - } - - - /** - * 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 - */ - private void sendBroadcastDownloadFinished(DownloadFileOperation download, RemoteOperationResult downloadResult) { - Intent end = new Intent(DOWNLOAD_FINISH_MESSAGE); - 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()); - sendStickyBroadcast(end); - } - - - /** - * Sends a broadcast when a new download is added to the queue. - * - * @param download Added download operation - */ - private void sendBroadcastNewDownload(DownloadFileOperation download) { - Intent added = new Intent(DOWNLOAD_ADDED_MESSAGE); - added.putExtra(ACCOUNT_NAME, download.getAccount().name); - added.putExtra(EXTRA_REMOTE_PATH, download.getRemotePath()); - added.putExtra(EXTRA_FILE_PATH, download.getSavePath()); - sendStickyBroadcast(added); - } - -} +/* ownCloud Android client application + * Copyright (C) 2012 Bartek Przybylski + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.owncloud.android.files.services; + +import java.io.File; +import java.util.AbstractList; ++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.datamodel.FileDataStorageManager; +import com.owncloud.android.datamodel.OCFile; +import eu.alefzero.webdav.OnDatatransferProgressListener; + +import com.owncloud.android.network.OwnCloudClientUtils; +import com.owncloud.android.operations.DownloadFileOperation; +import com.owncloud.android.operations.RemoteOperationResult; +import com.owncloud.android.ui.activity.FileDetailActivity; +import com.owncloud.android.ui.fragment.FileDetailFragment; ++import com.owncloud.android.ui.preview.PreviewImageActivity; ++import com.owncloud.android.ui.preview.PreviewImageFragment; + +import android.accounts.Account; +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.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Process; - import android.util.Log; +import android.widget.RemoteViews; + +import com.owncloud.android.Log_OC; +import com.owncloud.android.R; +import eu.alefzero.webdav.WebdavClient; + +public class FileDownloader extends Service implements OnDatatransferProgressListener { + + public static final String EXTRA_ACCOUNT = "ACCOUNT"; + public static final String EXTRA_FILE = "FILE"; + + public static final String DOWNLOAD_ADDED_MESSAGE = "DOWNLOAD_ADDED"; + public 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 ACCOUNT_NAME = "ACCOUNT_NAME"; + + private static final String TAG = "FileDownloader"; + + private Looper mServiceLooper; + private ServiceHandler mServiceHandler; + private IBinder mBinder; + private WebdavClient mDownloadClient = null; + private Account mLastAccount = null; + private FileDataStorageManager mStorageManager; + + private ConcurrentMap mPendingDownloads = new ConcurrentHashMap(); + private DownloadFileOperation mCurrentDownload = null; + + private NotificationManager mNotificationManager; + private Notification mNotification; + private int mLastPercent; + + + /** + * 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(); + } + + + /** + * Service initialization + */ + @Override + public void onCreate() { + super.onCreate(); + 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(); + } + + + /** + * 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) { + 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 requestedDownloads = new Vector(); // 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); + } + + 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 arg0) { + 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) ++ } ++ + + /** + * 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 { ++ 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 mBoundListeners = new 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 + */ + public void cancel(Account account, OCFile file) { + DownloadFileOperation download = null; + synchronized (mPendingDownloads) { + download = mPendingDownloads.remove(buildRemoteName(account, file)); + } + if (download != null) { + download.cancel(); + } + } + + ++ 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 some of its descendant files is downloading or waiting to download. + * + * @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.isDirectory()) { + // this can be slow if there are many downloads :( + Iterator it = mPendingDownloads.keySet().iterator(); + boolean found = false; + while (it.hasNext() && !found) { + found = it.next().startsWith(targetKey); + } + return found; + } else { + return (mPendingDownloads.containsKey(targetKey)); + } + } + } ++ ++ ++ /** ++ * Adds a listener interested in the progress of the download for a concrete file. ++ * ++ * @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. ++ */ ++ 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); ++ } ++ ++ ++ ++ /** ++ * Removes a listener interested in the progress of the download for a concrete file. ++ * ++ * @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. ++ */ ++ 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); ++ } ++ } ++ ++ ++ @Override ++ public void onTransferProgress(long progressRate) { ++ // old way, should not be in use any more ++ } ++ ++ ++ @Override ++ public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, ++ String fileName) { ++ String key = buildRemoteName(mCurrentDownload.getAccount(), mCurrentDownload.getFile()); ++ OnDatatransferProgressListener boundListener = mBoundListeners.get(key); ++ 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 requestedDownloads = (AbstractList) msg.obj; + if (msg.obj != null) { + Iterator it = requestedDownloads.iterator(); + while (it.hasNext()) { + mService.downloadFile(it.next()); + } + } + mService.stopSelf(msg.arg1); + } + } + + + + /** + * 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) { + + synchronized(mPendingDownloads) { + mCurrentDownload = mPendingDownloads.get(downloadKey); + } + + if (mCurrentDownload != null) { + + notifyDownloadStart(mCurrentDownload); + + /// 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()); + mDownloadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext()); + } + + /// perform the download + RemoteOperationResult downloadResult = null; + try { + downloadResult = mCurrentDownload.execute(mDownloadClient); + if (downloadResult.isSuccess()) { + saveDownloadedFile(); + } + + } finally { + synchronized(mPendingDownloads) { + mPendingDownloads.remove(downloadKey); + } + } + + + /// notify result + notifyDownloadResult(mCurrentDownload, downloadResult); + + sendBroadcastDownloadFinished(mCurrentDownload, downloadResult); + } + } + + + /** + * Updates the OC File after a successful download. + */ + private void saveDownloadedFile() { + OCFile file = mCurrentDownload.getFile(); + long syncDate = System.currentTimeMillis(); + file.setLastSyncDateForProperties(syncDate); + file.setLastSyncDateForData(syncDate); + file.setModificationTimestamp(mCurrentDownload.getModificationTimestamp()); + file.setModificationTimestampAtLastSyncForData(mCurrentDownload.getModificationTimestamp()); + // file.setEtag(mCurrentDownload.getEtag()); // TODO Etag, where available + file.setMimetype(mCurrentDownload.getMimeType()); + file.setStoragePath(mCurrentDownload.getSavePath()); + file.setFileLength((new File(mCurrentDownload.getSavePath()).length())); + mStorageManager.saveFile(file); + } + + + /** + * 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; + mNotification = new Notification(R.drawable.icon, getString(R.string.downloader_download_in_progress_ticker), System.currentTimeMillis()); + mNotification.flags |= Notification.FLAG_ONGOING_EVENT; + mNotification.contentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.progressbar_layout); + mNotification.contentView.setProgressBar(R.id.status_progress, 100, 0, download.getSize() < 0); + mNotification.contentView.setTextViewText(R.id.status_text, String.format(getString(R.string.downloader_download_in_progress_content), 0, new File(download.getSavePath()).getName())); + mNotification.contentView.setImageViewResource(R.id.status_icon, R.drawable.icon); + + /// includes a pending intent in the notification showing the details view of the file - Intent showDetailsIntent = new Intent(this, FileDetailActivity.class); ++ Intent showDetailsIntent = null; ++ if (PreviewImageFragment.canBePreviewed(download.getFile())) { ++ showDetailsIntent = new Intent(this, PreviewImageActivity.class); ++ } else { ++ showDetailsIntent = new Intent(this, FileDetailActivity.class); ++ } + showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, download.getFile()); + showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, download.getAccount()); + showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), showDetailsIntent, 0); + + mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotification); + } + + + /** + * Callback method to update the progress bar in the status notification. + */ + @Override + public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String fileName) { + int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer)); + if (percent != mLastPercent) { + mNotification.contentView.setProgressBar(R.id.status_progress, 100, percent, totalToTransfer < 0); + String text = String.format(getString(R.string.downloader_download_in_progress_content), percent, fileName); + mNotification.contentView.setTextViewText(R.id.status_text, text); + mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotification); + } + mLastPercent = percent; + } + + + /** + * Callback method to update the progress bar in the status notification (old version) + */ + @Override + public void onTransferProgress(long progressRate) { + // NOTHING TO DO HERE ANYMORE + } + + + /** + * Updates the status notification with the result of a download operation. + * + * @param downloadResult Result of the download operation. + * @param download Finished download operation + */ + private void notifyDownloadResult(DownloadFileOperation download, RemoteOperationResult downloadResult) { + mNotificationManager.cancel(R.string.downloader_download_in_progress_ticker); + if (!downloadResult.isCancelled()) { + int tickerId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_ticker : R.string.downloader_download_failed_ticker; + int contentId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_content : R.string.downloader_download_failed_content; + Notification finalNotification = new Notification(R.drawable.icon, getString(tickerId), System.currentTimeMillis()); + finalNotification.flags |= Notification.FLAG_AUTO_CANCEL; - // TODO put something smart in the contentIntent below - finalNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0); ++ Intent showDetailsIntent = null; ++ if (downloadResult.isSuccess()) { ++ if (PreviewImageFragment.canBePreviewed(download.getFile())) { ++ showDetailsIntent = new Intent(this, PreviewImageActivity.class); ++ } else { ++ showDetailsIntent = new Intent(this, FileDetailActivity.class); ++ } ++ showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, download.getFile()); ++ showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, download.getAccount()); ++ showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); ++ ++ } else { ++ // TODO put something smart in showDetailsIntent ++ showDetailsIntent = new Intent(); ++ } ++ finalNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), showDetailsIntent, 0); + finalNotification.setLatestEventInfo(getApplicationContext(), getString(tickerId), String.format(getString(contentId), new File(download.getSavePath()).getName()), finalNotification.contentIntent); + mNotificationManager.notify(tickerId, finalNotification); + } + } + + + /** + * 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 + */ + private void sendBroadcastDownloadFinished(DownloadFileOperation download, RemoteOperationResult downloadResult) { + Intent end = new Intent(DOWNLOAD_FINISH_MESSAGE); + 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()); + sendStickyBroadcast(end); + } + + + /** + * Sends a broadcast when a new download is added to the queue. + * + * @param download Added download operation + */ + private void sendBroadcastNewDownload(DownloadFileOperation download) { + Intent added = new Intent(DOWNLOAD_ADDED_MESSAGE); - /*added.putExtra(ACCOUNT_NAME, download.getAccount().name); - added.putExtra(EXTRA_REMOTE_PATH, download.getRemotePath());*/ ++ added.putExtra(ACCOUNT_NAME, download.getAccount().name); ++ added.putExtra(EXTRA_REMOTE_PATH, download.getRemotePath()); + added.putExtra(EXTRA_FILE_PATH, download.getSavePath()); + sendStickyBroadcast(added); + } + +} diff --cc src/com/owncloud/android/files/services/InstantUploadService.java index 8ec0a8d0,8ebcef44..b2eab0da --- a/src/com/owncloud/android/files/services/InstantUploadService.java +++ b/src/com/owncloud/android/files/services/InstantUploadService.java @@@ -23,16 -23,16 +23,16 @@@ import java.util.HashMap import java.util.LinkedList; import java.util.List; +import com.owncloud.android.Log_OC; ++import com.owncloud.android.utils.FileStorageUtils; +import com.owncloud.android.network.OwnCloudClientUtils; + +import eu.alefzero.webdav.WebdavClient; + import android.accounts.Account; import android.app.Service; import android.content.Intent; import android.os.IBinder; --import android.util.Log; - -import com.owncloud.android.network.OwnCloudClientUtils; -import com.owncloud.android.utils.FileStorageUtils; - -import eu.alefzero.webdav.WebdavClient; public class InstantUploadService extends Service { @@@ -57,10 -57,10 +57,10 @@@ if (intent == null || !intent.hasExtra(KEY_ACCOUNT) || !intent.hasExtra(KEY_DISPLAY_NAME) || !intent.hasExtra(KEY_FILE_PATH) || !intent.hasExtra(KEY_FILE_SIZE) || !intent.hasExtra(KEY_MIME_TYPE)) { - Log.w(TAG, "Not all required information was provided, abording"); + Log_OC.w(TAG, "Not all required information was provided, abording"); return Service.START_NOT_STICKY; } - + if (mUploaderRunnable == null) { mUploaderRunnable = new UploaderRunnable(); } @@@ -70,22 -70,22 +70,22 @@@ String mimetype = intent.getStringExtra(KEY_MIME_TYPE); Account account = intent.getParcelableExtra(KEY_ACCOUNT); long filesize = intent.getLongExtra(KEY_FILE_SIZE, -1); - + mUploaderRunnable.addElementToQueue(filename, filepath, mimetype, filesize, account); - + // starting new thread for new download doesnt seems like a good idea // maybe some thread pool or single background thread would be better - Log.d(TAG, "Starting instant upload thread"); + Log_OC.d(TAG, "Starting instant upload thread"); new Thread(mUploaderRunnable).start(); - + return Service.START_STICKY; } - + private class UploaderRunnable implements Runnable { - + Object mLock; List> mHashMapList; - + public UploaderRunnable() { mHashMapList = new LinkedList>(); mLock = new Object(); diff --cc src/com/owncloud/android/operations/ChunkedUploadFileOperation.java index 0e9838e6,d421803a..647bec08 --- a/src/com/owncloud/android/operations/ChunkedUploadFileOperation.java +++ b/src/com/owncloud/android/operations/ChunkedUploadFileOperation.java @@@ -28,8 -28,8 +28,9 @@@ import java.util.Random import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.methods.PutMethod; +import com.owncloud.android.Log_OC; import com.owncloud.android.datamodel.OCFile; + import com.owncloud.android.network.ProgressiveDataTransferer; import android.accounts.Account; import android.util.Log; @@@ -71,11 -71,11 +72,11 @@@ public class ChunkedUploadFileOperatio for (int chunkIndex = 0; chunkIndex < chunkCount ; chunkIndex++, offset += CHUNK_SIZE) { mPutMethod = new PutMethod(uriPrefix + chunkCount + "-" + chunkIndex); mPutMethod.addRequestHeader(OC_CHUNKED_HEADER, OC_CHUNKED_HEADER); - entity.setOffset(offset); - mPutMethod.setRequestEntity(entity); + ((ChunkFromFileChannelRequestEntity)mEntity).setOffset(offset); + mPutMethod.setRequestEntity(mEntity); status = client.executeMethod(mPutMethod); client.exhaustResponse(mPutMethod.getResponseBodyAsStream()); - Log.d(TAG, "Upload of " + getStoragePath() + " to " + getRemotePath() + ", chunk index " + chunkIndex + ", count " + chunkCount + ", HTTP result status " + status); + Log_OC.d(TAG, "Upload of " + getStoragePath() + " to " + getRemotePath() + ", chunk index " + chunkIndex + ", count " + chunkCount + ", HTTP result status " + status); if (!isSuccess(status)) break; } diff --cc src/com/owncloud/android/operations/SynchronizeFileOperation.java index 8b1857fe,4ed56d75..0a387ae9 --- a/src/com/owncloud/android/operations/SynchronizeFileOperation.java +++ b/src/com/owncloud/android/operations/SynchronizeFileOperation.java @@@ -163,7 -162,7 +163,7 @@@ public class SynchronizeFileOperation e } catch (Exception e) { result = new RemoteOperationResult(e); - Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", file " + mLocalFile.getRemotePath() + ": " + result.getLogMessage(), result.getException()); - Log.e(TAG, "Synchronizing " + mAccount.name + ", file " + (mLocalFile != null ? mLocalFile.getRemotePath() : "NULL") + ": " + result.getLogMessage(), result.getException()); ++ Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", file " + (mLocalFile != null ? mLocalFile.getRemotePath() : "NULL") + ": " + result.getLogMessage(), result.getException()); } finally { if (propfind != null) diff --cc src/com/owncloud/android/operations/UploadFileOperation.java index 704a88a8,0dfb72c3..77c0e076 --- a/src/com/owncloud/android/operations/UploadFileOperation.java +++ b/src/com/owncloud/android/operations/UploadFileOperation.java @@@ -30,11 -30,15 +30,15 @@@ import java.util.concurrent.atomic.Atom import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.methods.PutMethod; + import org.apache.commons.httpclient.methods.RequestEntity; import org.apache.http.HttpStatus; +import com.owncloud.android.Log_OC; + import android.accounts.Account; -import android.util.Log; + import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.files.services.FileUploader; + import com.owncloud.android.network.ProgressiveDataTransferer; import com.owncloud.android.operations.RemoteOperation; import com.owncloud.android.operations.RemoteOperationResult; import com.owncloud.android.operations.RemoteOperationResult.ResultCode; @@@ -210,17 -240,21 +240,19 @@@ public class UploadFileOperation extend } catch (Exception e) { result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_COPIED); return result; - + } finally { try { - if (in != null) in.close(); + if (in != null) + in.close(); } catch (Exception e) { - Log.d(TAG, "Weird exception while closing input stream for " + mOriginalStoragePath - + " (ignoring)", e); + Log_OC.d(TAG, "Weird exception while closing input stream for " + mOriginalStoragePath + " (ignoring)", e); } try { - if (out != null) out.close(); + if (out != null) + out.close(); } catch (Exception e) { - Log.d(TAG, "Weird exception while closing output stream for " + expectedPath - + " (ignoring)", e); + Log_OC.d(TAG, "Weird exception while closing output stream for " + expectedPath + " (ignoring)", e); } } } @@@ -283,19 -322,23 +320,19 @@@ temporalFile.delete(); } if (result.isSuccess()) { - Log.i(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage()); - + Log_OC.i(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage()); - } else { if (result.getException() != null) { String complement = ""; if (!nameCheckPassed) { complement = " (while checking file existence in server)"; } else if (!localCopyPassed) { - complement = " (while copying local file to " + FileStorageUtils.getSavePath(mAccount.name) + ")"; + complement = " (while copying local file to " + FileStorageUtils.getSavePath(mAccount.name) + + ")"; } - Log.e(TAG, - "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage() - + complement, result.getException()); + Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage() + complement, result.getException()); } else { - Log.e(TAG, - "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage()); + Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage()); } } } diff --cc src/com/owncloud/android/ui/activity/FileDetailActivity.java index f08d1605,19337356..12c133c5 --- a/src/com/owncloud/android/ui/activity/FileDetailActivity.java +++ b/src/com/owncloud/android/ui/activity/FileDetailActivity.java @@@ -1,231 -1,395 +1,392 @@@ -/* ownCloud Android client application - * Copyright (C) 2011 Bartek Przybylski - * Copyright (C) 2012-2013 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -package com.owncloud.android.ui.activity; - -import android.accounts.Account; -import android.app.Dialog; -import android.app.ProgressDialog; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.ServiceConnection; -import android.content.res.Configuration; -import android.os.Bundle; -import android.os.IBinder; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentTransaction; -import android.util.Log; - -import com.actionbarsherlock.app.ActionBar; -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.MenuItem; -import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.files.services.FileDownloader; -import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; -import com.owncloud.android.files.services.FileUploader; -import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; -import com.owncloud.android.ui.fragment.FileDetailFragment; -import com.owncloud.android.ui.fragment.FileFragment; -import com.owncloud.android.ui.preview.PreviewMediaFragment; - -import com.owncloud.android.AccountUtils; -import com.owncloud.android.R; - -/** - * This activity displays the details of a file like its name, its size and so - * on. - * - * @author Bartek Przybylski - * @author David A. Velasco - */ -public class FileDetailActivity extends SherlockFragmentActivity implements FileFragment.ContainerActivity { - - public static final int DIALOG_SHORT_WAIT = 0; - - public static final String TAG = FileDetailActivity.class.getSimpleName(); - - public static final String EXTRA_MODE = "MODE"; - public static final int MODE_DETAILS = 0; - public static final int MODE_PREVIEW = 1; - - public static final String KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW"; - - private boolean mConfigurationChangedToLandscape = false; - private FileDownloaderBinder mDownloaderBinder = null; - private ServiceConnection mDownloadConnection, mUploadConnection = null; - private FileUploaderBinder mUploaderBinder = null; - private boolean mWaitingToPreview; - - private OCFile mFile; - private Account mAccount; - - private FileDataStorageManager mStorageManager; - private DownloadFinishReceiver mDownloadFinishReceiver; - - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mFile = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE); - mAccount = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_ACCOUNT); - mStorageManager = new FileDataStorageManager(mAccount, getContentResolver()); - - // check if configuration changed to large-land ; for a tablet being changed from portrait to landscape when in FileDetailActivity - Configuration conf = getResources().getConfiguration(); - mConfigurationChangedToLandscape = (conf.orientation == Configuration.ORIENTATION_LANDSCAPE && - (conf.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE - ); - - if (!mConfigurationChangedToLandscape) { - setContentView(R.layout.file_activity_details); - - ActionBar actionBar = getSupportActionBar(); - actionBar.setDisplayHomeAsUpEnabled(true); - - if (savedInstanceState == null) { - mWaitingToPreview = false; - createChildFragment(); - } else { - mWaitingToPreview = savedInstanceState.getBoolean(KEY_WAITING_TO_PREVIEW); - } - - mDownloadConnection = new DetailsServiceConnection(); - bindService(new Intent(this, FileDownloader.class), mDownloadConnection, Context.BIND_AUTO_CREATE); - mUploadConnection = new DetailsServiceConnection(); - bindService(new Intent(this, FileUploader.class), mUploadConnection, Context.BIND_AUTO_CREATE); - - - } else { - backToDisplayActivity(false); // the 'back' won't be effective until this.onStart() and this.onResume() are completed; - } - - - } - - /** - * Creates the proper fragment depending upon the state of the handled {@link OCFile} and - * the requested {@link Intent}. - */ - private void createChildFragment() { - int mode = getIntent().getIntExtra(EXTRA_MODE, MODE_PREVIEW); - - Fragment newFragment = null; - if (PreviewMediaFragment.canBePreviewed(mFile) && mode == MODE_PREVIEW) { - if (mFile.isDown()) { - newFragment = new PreviewMediaFragment(mFile, mAccount); - - } else { - newFragment = new FileDetailFragment(mFile, mAccount); - mWaitingToPreview = true; - } - - } else { - newFragment = new FileDetailFragment(mFile, mAccount); - } - FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); - ft.replace(R.id.fragment, newFragment, FileDetailFragment.FTAG); - ft.commit(); - } - - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putBoolean(KEY_WAITING_TO_PREVIEW, mWaitingToPreview); - } - - - @Override - public void onPause() { - super.onPause(); - if (mDownloadFinishReceiver != null) { - unregisterReceiver(mDownloadFinishReceiver); - mDownloadFinishReceiver = null; - } - } - - - @Override - public void onResume() { - super.onResume(); - if (!mConfigurationChangedToLandscape) { - // TODO this is probably unnecessary - Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG); - if (fragment != null && fragment instanceof FileDetailFragment) { - ((FileDetailFragment) fragment).updateFileDetails(false, false); - } - } - // Listen for download messages - IntentFilter downloadIntentFilter = new IntentFilter(FileDownloader.DOWNLOAD_ADDED_MESSAGE); - downloadIntentFilter.addAction(FileDownloader.DOWNLOAD_FINISH_MESSAGE); - mDownloadFinishReceiver = new DownloadFinishReceiver(); - registerReceiver(mDownloadFinishReceiver, downloadIntentFilter); - } - - - /** Defines callbacks for service binding, passed to bindService() */ - private class DetailsServiceConnection implements ServiceConnection { - - @Override - public void onServiceConnected(ComponentName component, IBinder service) { - - if (component.equals(new ComponentName(FileDetailActivity.this, FileDownloader.class))) { - Log.d(TAG, "Download service connected"); - mDownloaderBinder = (FileDownloaderBinder) service; - if (mWaitingToPreview) { - requestForDownload(); - } - - } else if (component.equals(new ComponentName(FileDetailActivity.this, FileUploader.class))) { - Log.d(TAG, "Upload service connected"); - mUploaderBinder = (FileUploaderBinder) service; - } else { - return; - } - - Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG); - FileDetailFragment detailsFragment = (fragment instanceof FileDetailFragment) ? (FileDetailFragment) fragment : null; - if (detailsFragment != null) { - detailsFragment.listenForTransferProgress(); - detailsFragment.updateFileDetails(mWaitingToPreview, false); // let the fragment gets the mDownloadBinder through getDownloadBinder() (see FileDetailFragment#updateFileDetais()) - } - } - - @Override - public void onServiceDisconnected(ComponentName component) { - if (component.equals(new ComponentName(FileDetailActivity.this, FileDownloader.class))) { - Log.d(TAG, "Download service disconnected"); - mDownloaderBinder = null; - } else if (component.equals(new ComponentName(FileDetailActivity.this, FileUploader.class))) { - Log.d(TAG, "Upload service disconnected"); - mUploaderBinder = null; - } - } - }; - - - @Override - public void onDestroy() { - super.onDestroy(); - if (mDownloadConnection != null) { - unbindService(mDownloadConnection); - mDownloadConnection = null; - } - if (mUploadConnection != null) { - unbindService(mUploadConnection); - mUploadConnection = null; - } - } - - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - boolean returnValue = false; - - switch(item.getItemId()){ - case android.R.id.home: - backToDisplayActivity(true); - returnValue = true; - break; - default: - returnValue = super.onOptionsItemSelected(item); - } - - return returnValue; - } - - - - private void backToDisplayActivity(boolean moveToParent) { - Intent intent = new Intent(this, FileDisplayActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - OCFile targetFile = null; - if (mFile != null) { - targetFile = moveToParent ? mStorageManager.getFileById(mFile.getParentId()) : mFile; - } - intent.putExtra(FileDetailFragment.EXTRA_FILE, targetFile); - intent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, mAccount); - startActivity(intent); - finish(); - } - - - @Override - protected Dialog onCreateDialog(int id) { - Dialog dialog = null; - switch (id) { - case DIALOG_SHORT_WAIT: { - ProgressDialog working_dialog = new ProgressDialog(this); - working_dialog.setMessage(getResources().getString( - R.string.wait_a_moment)); - working_dialog.setIndeterminate(true); - working_dialog.setCancelable(false); - dialog = working_dialog; - break; - } - default: - dialog = null; - } - return dialog; - } - - - /** - * {@inheritDoc} - */ - @Override - public void onFileStateChanged() { - // nothing to do here! - } - - - /** - * {@inheritDoc} - */ - @Override - public FileDownloaderBinder getFileDownloaderBinder() { - return mDownloaderBinder; - } - - - @Override - public FileUploaderBinder getFileUploaderBinder() { - return mUploaderBinder; - } - - - @Override - public void showFragmentWithDetails(OCFile file) { - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.fragment, new FileDetailFragment(file, mAccount), FileDetailFragment.FTAG); - transaction.commit(); - } - - - private void requestForDownload() { - if (!mDownloaderBinder.isDownloading(mAccount, mFile)) { - Intent i = new Intent(this, FileDownloader.class); - i.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount); - i.putExtra(FileDownloader.EXTRA_FILE, mFile); - startService(i); - } - } - - - /** - * Class waiting for broadcast events from the {@link FielDownloader} service. - * - * Updates the UI when a download is started or finished, provided that it is relevant for the - * current file. - */ - private class DownloadFinishReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - boolean sameAccount = isSameAccount(context, intent); - String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH); - boolean samePath = (mFile != null && mFile.getRemotePath().equals(downloadedRemotePath)); - - if (sameAccount && samePath) { - updateChildFragment(intent.getAction(), downloadedRemotePath, intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false)); - } - - removeStickyBroadcast(intent); - } - - private boolean isSameAccount(Context context, Intent intent) { - String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME); - return (accountName != null && accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name)); - } - } - - - public void updateChildFragment(String downloadEvent, String downloadedRemotePath, boolean success) { - Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG); - if (fragment != null && fragment instanceof FileDetailFragment) { - FileDetailFragment detailsFragment = (FileDetailFragment) fragment; - OCFile fileInFragment = detailsFragment.getFile(); - if (fileInFragment != null && !downloadedRemotePath.equals(fileInFragment.getRemotePath())) { - // this never should happen; fileInFragment should be always equals to mFile, that was compared to downloadedRemotePath in DownloadReceiver - mWaitingToPreview = false; - - } else if (downloadEvent.equals(FileDownloader.DOWNLOAD_ADDED_MESSAGE)) { - // grants that the progress bar is updated - detailsFragment.listenForTransferProgress(); - detailsFragment.updateFileDetails(true, false); - - } else if (downloadEvent.equals(FileDownloader.DOWNLOAD_FINISH_MESSAGE)) { - // refresh the details fragment - if (success && mWaitingToPreview) { - mFile = mStorageManager.getFileById(mFile.getFileId()); // update the file from database, for the local storage path - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.fragment, new PreviewMediaFragment(mFile, mAccount), FileDetailFragment.FTAG); - transaction.commit(); - mWaitingToPreview = false; - - } else { - detailsFragment.updateFileDetails(false, (success)); - // TODO error message if !success ¿? - } - } - } // TODO else if (fragment != null && fragment ) - - - } - -} +/* ownCloud Android client application + * Copyright (C) 2011 Bartek Przybylski + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.owncloud.android.ui.activity; + +import android.accounts.Account; +import android.app.Dialog; +import android.app.ProgressDialog; ++import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; ++import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.content.res.Configuration; +import android.os.Bundle; +import android.os.IBinder; ++import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentTransaction; +import android.util.Log; + +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.MenuItem; ++import com.owncloud.android.datamodel.FileDataStorageManager; +import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.files.services.FileDownloader; +import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; +import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; +import com.owncloud.android.ui.fragment.FileDetailFragment; - ++import com.owncloud.android.ui.fragment.FileFragment; ++import com.owncloud.android.ui.preview.PreviewMediaFragment; ++import com.owncloud.android.AccountUtils; +import com.owncloud.android.Log_OC; ++ +import com.owncloud.android.R; + +/** + * This activity displays the details of a file like its name, its size and so + * on. + * + * @author Bartek Przybylski - * ++ * @author David A. Velasco + */ - public class FileDetailActivity extends SherlockFragmentActivity implements FileDetailFragment.ContainerActivity { ++public class FileDetailActivity extends SherlockFragmentActivity implements FileFragment.ContainerActivity { + + public static final int DIALOG_SHORT_WAIT = 0; + + public static final String TAG = FileDetailActivity.class.getSimpleName(); + ++ public static final String EXTRA_MODE = "MODE"; ++ public static final int MODE_DETAILS = 0; ++ public static final int MODE_PREVIEW = 1; ++ ++ public static final String KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW"; ++ + private boolean mConfigurationChangedToLandscape = false; + private FileDownloaderBinder mDownloaderBinder = null; + private ServiceConnection mDownloadConnection, mUploadConnection = null; + private FileUploaderBinder mUploaderBinder = null; ++ private boolean mWaitingToPreview; ++ ++ private OCFile mFile; ++ private Account mAccount; + ++ private FileDataStorageManager mStorageManager; ++ private DownloadFinishReceiver mDownloadFinishReceiver; ++ + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + ++ mFile = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE); ++ mAccount = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_ACCOUNT); ++ mStorageManager = new FileDataStorageManager(mAccount, getContentResolver()); ++ + // check if configuration changed to large-land ; for a tablet being changed from portrait to landscape when in FileDetailActivity + Configuration conf = getResources().getConfiguration(); + mConfigurationChangedToLandscape = (conf.orientation == Configuration.ORIENTATION_LANDSCAPE && + (conf.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE + ); + + if (!mConfigurationChangedToLandscape) { - mDownloadConnection = new DetailsServiceConnection(); - bindService(new Intent(this, FileDownloader.class), mDownloadConnection, Context.BIND_AUTO_CREATE); - mUploadConnection = new DetailsServiceConnection(); - bindService(new Intent(this, FileUploader.class), mUploadConnection, Context.BIND_AUTO_CREATE); - + setContentView(R.layout.file_activity_details); + + ActionBar actionBar = getSupportActionBar(); + actionBar.setDisplayHomeAsUpEnabled(true); + - OCFile file = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE); - Account account = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_ACCOUNT); - FileDetailFragment mFileDetail = new FileDetailFragment(file, account); - - FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); - ft.replace(R.id.fragment, mFileDetail, FileDetailFragment.FTAG); - ft.commit(); ++ if (savedInstanceState == null) { ++ mWaitingToPreview = false; ++ createChildFragment(); ++ } else { ++ mWaitingToPreview = savedInstanceState.getBoolean(KEY_WAITING_TO_PREVIEW); ++ } ++ ++ mDownloadConnection = new DetailsServiceConnection(); ++ bindService(new Intent(this, FileDownloader.class), mDownloadConnection, Context.BIND_AUTO_CREATE); ++ mUploadConnection = new DetailsServiceConnection(); ++ bindService(new Intent(this, FileUploader.class), mUploadConnection, Context.BIND_AUTO_CREATE); ++ + + } else { - backToDisplayActivity(); // the 'back' won't be effective until this.onStart() and this.onResume() are completed; ++ backToDisplayActivity(false); // the 'back' won't be effective until this.onStart() and this.onResume() are completed; + } + ++ } ++ ++ /** ++ * Creates the proper fragment depending upon the state of the handled {@link OCFile} and ++ * the requested {@link Intent}. ++ */ ++ private void createChildFragment() { ++ int mode = getIntent().getIntExtra(EXTRA_MODE, MODE_PREVIEW); + ++ Fragment newFragment = null; ++ if (PreviewMediaFragment.canBePreviewed(mFile) && mode == MODE_PREVIEW) { ++ if (mFile.isDown()) { ++ newFragment = new PreviewMediaFragment(mFile, mAccount); ++ ++ } else { ++ newFragment = new FileDetailFragment(mFile, mAccount); ++ mWaitingToPreview = true; ++ } ++ ++ } else { ++ newFragment = new FileDetailFragment(mFile, mAccount); ++ } ++ FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ++ ft.replace(R.id.fragment, newFragment, FileDetailFragment.FTAG); ++ ft.commit(); ++ } ++ ++ ++ @Override ++ protected void onSaveInstanceState(Bundle outState) { ++ super.onSaveInstanceState(outState); ++ outState.putBoolean(KEY_WAITING_TO_PREVIEW, mWaitingToPreview); ++ } ++ ++ ++ @Override ++ public void onPause() { ++ super.onPause(); ++ if (mDownloadFinishReceiver != null) { ++ unregisterReceiver(mDownloadFinishReceiver); ++ mDownloadFinishReceiver = null; ++ } ++ } ++ ++ ++ @Override ++ public void onResume() { ++ super.onResume(); ++ if (!mConfigurationChangedToLandscape) { ++ // TODO this is probably unnecessary ++ Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG); ++ if (fragment != null && fragment instanceof FileDetailFragment) { ++ ((FileDetailFragment) fragment).updateFileDetails(false, false); ++ } ++ } ++ // Listen for download messages ++ IntentFilter downloadIntentFilter = new IntentFilter(FileDownloader.DOWNLOAD_ADDED_MESSAGE); ++ downloadIntentFilter.addAction(FileDownloader.DOWNLOAD_FINISH_MESSAGE); ++ mDownloadFinishReceiver = new DownloadFinishReceiver(); ++ registerReceiver(mDownloadFinishReceiver, downloadIntentFilter); + } + + + /** Defines callbacks for service binding, passed to bindService() */ + private class DetailsServiceConnection implements ServiceConnection { + + @Override + public void onServiceConnected(ComponentName component, IBinder service) { ++ + if (component.equals(new ComponentName(FileDetailActivity.this, FileDownloader.class))) { + Log_OC.d(TAG, "Download service connected"); + mDownloaderBinder = (FileDownloaderBinder) service; ++ if (mWaitingToPreview) { ++ requestForDownload(); ++ } ++ + } else if (component.equals(new ComponentName(FileDetailActivity.this, FileUploader.class))) { + Log_OC.d(TAG, "Upload service connected"); + mUploaderBinder = (FileUploaderBinder) service; + } else { + return; + } - FileDetailFragment fragment = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG); - if (fragment != null) - fragment.updateFileDetails(false); // let the fragment gets the mDownloadBinder through getDownloadBinder() (see FileDetailFragment#updateFileDetais()) ++ ++ Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG); ++ FileDetailFragment detailsFragment = (fragment instanceof FileDetailFragment) ? (FileDetailFragment) fragment : null; ++ if (detailsFragment != null) { ++ detailsFragment.listenForTransferProgress(); ++ detailsFragment.updateFileDetails(mWaitingToPreview, false); // let the fragment gets the mDownloadBinder through getDownloadBinder() (see FileDetailFragment#updateFileDetais()) ++ } + } + + @Override + public void onServiceDisconnected(ComponentName component) { + if (component.equals(new ComponentName(FileDetailActivity.this, FileDownloader.class))) { + Log_OC.d(TAG, "Download service disconnected"); + mDownloaderBinder = null; + } else if (component.equals(new ComponentName(FileDetailActivity.this, FileUploader.class))) { + Log_OC.d(TAG, "Upload service disconnected"); + mUploaderBinder = null; + } + } + }; + - ++ + @Override + public void onDestroy() { + super.onDestroy(); + if (mDownloadConnection != null) { + unbindService(mDownloadConnection); + mDownloadConnection = null; + } + if (mUploadConnection != null) { + unbindService(mUploadConnection); + mUploadConnection = null; + } + } + + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + boolean returnValue = false; + + switch(item.getItemId()){ + case android.R.id.home: - backToDisplayActivity(); ++ backToDisplayActivity(true); + returnValue = true; + break; + default: - returnValue = super.onOptionsItemSelected(item); ++ returnValue = super.onOptionsItemSelected(item); + } + + return returnValue; + } + - - - @Override - protected void onResume() { - - super.onResume(); - if (!mConfigurationChangedToLandscape) { - FileDetailFragment fragment = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG); - fragment.updateFileDetails(false); - } - } - - - private void backToDisplayActivity() { ++ private void backToDisplayActivity(boolean moveToParent) { + Intent intent = new Intent(this, FileDisplayActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - intent.putExtra(FileDetailFragment.EXTRA_FILE, getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE)); - intent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, getIntent().getParcelableExtra(FileDetailFragment.EXTRA_ACCOUNT)); ++ OCFile targetFile = null; ++ if (mFile != null) { ++ targetFile = moveToParent ? mStorageManager.getFileById(mFile.getParentId()) : mFile; ++ } ++ intent.putExtra(FileDetailFragment.EXTRA_FILE, targetFile); ++ intent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, mAccount); + startActivity(intent); + finish(); + } + + + @Override + protected Dialog onCreateDialog(int id) { + Dialog dialog = null; + switch (id) { + case DIALOG_SHORT_WAIT: { + ProgressDialog working_dialog = new ProgressDialog(this); + working_dialog.setMessage(getResources().getString( + R.string.wait_a_moment)); + working_dialog.setIndeterminate(true); + working_dialog.setCancelable(false); + dialog = working_dialog; + break; + } + default: + dialog = null; + } + return dialog; + } + - ++ + /** + * {@inheritDoc} + */ + @Override + public void onFileStateChanged() { + // nothing to do here! + } + + + /** + * {@inheritDoc} + */ + @Override + public FileDownloaderBinder getFileDownloaderBinder() { + return mDownloaderBinder; + } + + + @Override + public FileUploaderBinder getFileUploaderBinder() { + return mUploaderBinder; + } ++ ++ ++ @Override ++ public void showFragmentWithDetails(OCFile file) { ++ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); ++ transaction.replace(R.id.fragment, new FileDetailFragment(file, mAccount), FileDetailFragment.FTAG); ++ transaction.commit(); ++ } ++ ++ ++ private void requestForDownload() { ++ if (!mDownloaderBinder.isDownloading(mAccount, mFile)) { ++ Intent i = new Intent(this, FileDownloader.class); ++ i.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount); ++ i.putExtra(FileDownloader.EXTRA_FILE, mFile); ++ startService(i); ++ } ++ } ++ + ++ /** ++ * Class waiting for broadcast events from the {@link FielDownloader} service. ++ * ++ * Updates the UI when a download is started or finished, provided that it is relevant for the ++ * current file. ++ */ ++ private class DownloadFinishReceiver extends BroadcastReceiver { ++ @Override ++ public void onReceive(Context context, Intent intent) { ++ boolean sameAccount = isSameAccount(context, intent); ++ String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH); ++ boolean samePath = (mFile != null && mFile.getRemotePath().equals(downloadedRemotePath)); ++ ++ if (sameAccount && samePath) { ++ updateChildFragment(intent.getAction(), downloadedRemotePath, intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false)); ++ } ++ ++ removeStickyBroadcast(intent); ++ } ++ ++ private boolean isSameAccount(Context context, Intent intent) { ++ String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME); ++ return (accountName != null && accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name)); ++ } ++ } ++ ++ ++ public void updateChildFragment(String downloadEvent, String downloadedRemotePath, boolean success) { ++ Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG); ++ if (fragment != null && fragment instanceof FileDetailFragment) { ++ FileDetailFragment detailsFragment = (FileDetailFragment) fragment; ++ OCFile fileInFragment = detailsFragment.getFile(); ++ if (fileInFragment != null && !downloadedRemotePath.equals(fileInFragment.getRemotePath())) { ++ // this never should happen; fileInFragment should be always equals to mFile, that was compared to downloadedRemotePath in DownloadReceiver ++ mWaitingToPreview = false; ++ ++ } else if (downloadEvent.equals(FileDownloader.DOWNLOAD_ADDED_MESSAGE)) { ++ // grants that the progress bar is updated ++ detailsFragment.listenForTransferProgress(); ++ detailsFragment.updateFileDetails(true, false); ++ ++ } else if (downloadEvent.equals(FileDownloader.DOWNLOAD_FINISH_MESSAGE)) { ++ // refresh the details fragment ++ if (success && mWaitingToPreview) { ++ mFile = mStorageManager.getFileById(mFile.getFileId()); // update the file from database, for the local storage path ++ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); ++ transaction.replace(R.id.fragment, new PreviewMediaFragment(mFile, mAccount), FileDetailFragment.FTAG); ++ transaction.commit(); ++ mWaitingToPreview = false; ++ ++ } else { ++ detailsFragment.updateFileDetails(false, (success)); ++ // TODO error message if !success ¿? ++ } ++ } ++ } // TODO else if (fragment != null && fragment ) ++ ++ } ++ +} diff --cc src/com/owncloud/android/ui/activity/FileDisplayActivity.java index cfdd89bb,8e36b9aa..d2545c67 --- a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java @@@ -132,14 -140,18 +141,17 @@@ public class FileDisplayActivity extend private static final int ACTION_SELECT_CONTENT_FROM_APPS = 1; private static final int ACTION_SELECT_MULTIPLE_FILES = 2; - private static final int ACTION_SELECT_FAILED_INSTANT_UPLOAD = 2; - + private static final String TAG = "FileDisplayActivity"; + private static int[] mMenuIdentifiersToPatch = {R.id.action_about_app}; + private OCFile mWaitingToPreview; + private Handler mHandler; + - @Override public void onCreate(Bundle savedInstanceState) { - Log.d(getClass().toString(), "onCreate() start"); + Log_OC.d(getClass().toString(), "onCreate() start"); super.onCreate(savedInstanceState); /// Load of parameters from received intent @@@ -214,9 -241,10 +241,10 @@@ // show changelog, if needed - showChangeLog(); + //showChangeLog(); + mBackFromCreatingFirstAccount = false; - Log.d(getClass().toString(), "onCreate() end"); + Log_OC.d(getClass().toString(), "onCreate() end"); } @@@ -318,7 -353,10 +353,8 @@@ @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getSherlock().getMenuInflater(); - inflater.inflate(R.menu.menu, menu); + inflater.inflate(R.menu.main_menu, menu); + - patchHiddenAccents(menu); - return true; } @@@ -326,31 -384,36 +362,32 @@@ public boolean onOptionsItemSelected(MenuItem item) { boolean retval = true; switch (item.getItemId()) { - case R.id.createDirectoryItem: { - showDialog(DIALOG_CREATE_DIR); - break; - } - case R.id.startSync: { - startSynchronization(); - break; - } - case R.id.action_upload: { - showDialog(DIALOG_CHOOSE_UPLOAD_SOURCE); - break; - } - case R.id.action_settings: { - Intent settingsIntent = new Intent(this, Preferences.class); - startActivity(settingsIntent); - break; - } - case android.R.id.home: { - if (mCurrentDir != null && mCurrentDir.getParentId() != 0) { - onBackPressed(); + case R.id.action_create_dir: { + EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.uploader_info_dirname), "", this); + dialog.show(getSupportFragmentManager(), "createdirdialog"); + break; } - break; - } - default: - retval = super.onOptionsItemSelected(item); + case R.id.action_sync_account: { + startSynchronization(); + break; + } + case R.id.action_upload: { + showDialog(DIALOG_CHOOSE_UPLOAD_SOURCE); + break; + } + case R.id.action_settings: { + Intent settingsIntent = new Intent(this, Preferences.class); + startActivity(settingsIntent); + break; + } - case R.id.action_about_app: { - showDialog(DIALOG_ABOUT_APP); - break; - } + case android.R.id.home: { + if(mCurrentDir != null && mCurrentDir.getParentId() != 0){ + onBackPressed(); + } + break; + } + default: + retval = super.onOptionsItemSelected(item); } return retval; } @@@ -517,12 -575,14 +549,14 @@@ } } } + outState.putParcelable(FileDetailActivity.KEY_WAITING_TO_PREVIEW, mWaitingToPreview); - Log.d(getClass().toString(), "onSaveInstanceState() end"); + Log_OC.d(getClass().toString(), "onSaveInstanceState() end"); } + @Override - public void onResume() { - Log.d(getClass().toString(), "onResume() start"); + protected void onResume() { + Log_OC.d(getClass().toString(), "onResume() start"); super.onResume(); if (AccountUtils.accountsAreSetup(this)) { @@@ -857,15 -952,18 +910,15 @@@ */ @Override public void onReceive(Context context, Intent intent) { - boolean inProgress = intent.getBooleanExtra( - FileSyncService.IN_PROGRESS, false); - String accountName = intent - .getStringExtra(FileSyncService.ACCOUNT_NAME); + boolean inProgress = intent.getBooleanExtra(FileSyncService.IN_PROGRESS, false); + String accountName = intent.getStringExtra(FileSyncService.ACCOUNT_NAME); - Log.d("FileDisplay", "sync of account " + accountName - + " is in_progress: " + inProgress); + Log_OC.d("FileDisplay", "sync of account " + accountName + " is in_progress: " + inProgress); - if (accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name)) { - - String synchFolderRemotePath = intent.getStringExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH); - + if (accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name)) { + + String synchFolderRemotePath = intent.getStringExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH); + boolean fillBlankRoot = false; if (mCurrentDir == null) { mCurrentDir = mStorageManager.getFileByPath("/"); @@@ -1053,10 -1252,14 +1207,14 @@@ @Override public void onServiceConnected(ComponentName component, IBinder service) { if (component.equals(new ComponentName(FileDisplayActivity.this, FileDownloader.class))) { - Log.d(TAG, "Download service connected"); + Log_OC.d(TAG, "Download service connected"); mDownloaderBinder = (FileDownloaderBinder) service; + if (mWaitingToPreview != null) { + requestForDownload(); + } + } else if (component.equals(new ComponentName(FileDisplayActivity.this, FileUploader.class))) { - Log.d(TAG, "Upload service connected"); + Log_OC.d(TAG, "Upload service connected"); mUploaderBinder = (FileUploaderBinder) service; } else { return; diff --cc src/com/owncloud/android/ui/activity/InstantUploadActivity.java index d5f87e6f,910802f9..eb0b4d5a --- a/src/com/owncloud/android/ui/activity/InstantUploadActivity.java +++ b/src/com/owncloud/android/ui/activity/InstantUploadActivity.java @@@ -122,13 -125,14 +126,14 @@@ public class InstantUploadActivity exte lastLoadImageIdx++; String imp_path = c.getString(1); + String message = c.getString(4); fileList.put(lastLoadImageIdx, imp_path); - LinearLayout rowLayout = getLinearLayout(lastLoadImageIdx); + LinearLayout rowLayout = getHorizontalLinearLayout(lastLoadImageIdx); rowLayout.addView(getFileCheckbox(lastLoadImageIdx)); rowLayout.addView(getImageButton(imp_path, lastLoadImageIdx)); - rowLayout.addView(getFileButton(imp_path, lastLoadImageIdx)); + rowLayout.addView(getFileButton(imp_path, message, lastLoadImageIdx)); listView.addView(rowLayout); - Log.d(LOG_TAG, imp_path + " on idx: " + lastLoadImageIdx); + Log_OC.d(LOG_TAG, imp_path + " on idx: " + lastLoadImageIdx); if (lastLoadImageIdx % MAX_LOAD_IMAGES == 0) { break; } diff --cc src/com/owncloud/android/ui/activity/UploadFilesActivity.java index 84fdc638,d16b2637..4b28ac11 --- a/src/com/owncloud/android/ui/activity/UploadFilesActivity.java +++ b/src/com/owncloud/android/ui/activity/UploadFilesActivity.java @@@ -371,18 -367,14 +368,14 @@@ public class UploadFilesActivity extend @Override public void onNeutral(String callerTag) { - Log.d(TAG, "Phantom neutral button in dialog was clicked; dialog tag is " + callerTag); + Log_OC.d(TAG, "Phantom neutral button in dialog was clicked; dialog tag is " + callerTag); - mCurrentDialog.dismiss(); - mCurrentDialog = null; } @Override public void onCancel(String callerTag) { /// nothing to do; don't finish, let the user change the selection - Log.d(TAG, "Negative button in dialog was clicked; dialog tag is " + callerTag); + Log_OC.d(TAG, "Negative button in dialog was clicked; dialog tag is " + callerTag); - mCurrentDialog.dismiss(); - mCurrentDialog = null; } diff --cc src/com/owncloud/android/ui/fragment/FileDetailFragment.java index 88c4889c,80dc4264..96218a6e --- a/src/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/com/owncloud/android/ui/fragment/FileDetailFragment.java @@@ -1,1063 -1,1037 +1,1058 @@@ -/* ownCloud Android client application - * Copyright (C) 2011 Bartek Przybylski - * Copyright (C) 2012-2013 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -package com.owncloud.android.ui.fragment; - -import java.io.File; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.httpclient.methods.GetMethod; -import org.apache.commons.httpclient.methods.PostMethod; -import org.apache.commons.httpclient.methods.StringRequestEntity; -import org.apache.commons.httpclient.params.HttpConnectionManagerParams; -import org.apache.http.HttpStatus; -import org.apache.http.NameValuePair; -import org.apache.http.client.utils.URLEncodedUtils; -import org.apache.http.message.BasicNameValuePair; -import org.apache.http.protocol.HTTP; -import org.apache.jackrabbit.webdav.client.methods.PropFindMethod; -import org.json.JSONObject; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.app.Activity; -import android.content.ActivityNotFoundException; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.support.v4.app.FragmentTransaction; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.webkit.MimeTypeMap; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.ImageView; -import android.widget.ProgressBar; -import android.widget.TextView; -import android.widget.Toast; - -import com.actionbarsherlock.app.SherlockFragment; -import com.owncloud.android.AccountUtils; -import com.owncloud.android.DisplayUtils; -import com.owncloud.android.authenticator.AccountAuthenticator; -import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.files.services.FileObserverService; -import com.owncloud.android.files.services.FileUploader; -import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; -import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; -import com.owncloud.android.network.OwnCloudClientUtils; -import com.owncloud.android.operations.OnRemoteOperationListener; -import com.owncloud.android.operations.RemoteOperation; -import com.owncloud.android.operations.RemoteOperationResult; -import com.owncloud.android.operations.RemoteOperationResult.ResultCode; -import com.owncloud.android.operations.RemoveFileOperation; -import com.owncloud.android.operations.RenameFileOperation; -import com.owncloud.android.operations.SynchronizeFileOperation; -import com.owncloud.android.ui.activity.ConflictsResolveActivity; -import com.owncloud.android.ui.activity.FileDetailActivity; -import com.owncloud.android.ui.activity.FileDisplayActivity; -import com.owncloud.android.ui.dialog.EditNameDialog; -import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener; -import com.owncloud.android.utils.OwnCloudVersion; - -import com.owncloud.android.R; - -import eu.alefzero.webdav.OnDatatransferProgressListener; -import eu.alefzero.webdav.WebdavClient; -import eu.alefzero.webdav.WebdavUtils; - -/** - * This Fragment is used to display the details about a file. - * - * @author Bartek Przybylski - * @author David A. Velasco - */ -public class FileDetailFragment extends SherlockFragment implements - OnClickListener, - ConfirmationDialogFragment.ConfirmationDialogFragmentListener, OnRemoteOperationListener, EditNameDialogListener, - FileFragment { - - public static final String EXTRA_FILE = "FILE"; - public static final String EXTRA_ACCOUNT = "ACCOUNT"; - - private FileFragment.ContainerActivity mContainerActivity; - - private int mLayout; - private View mView; - private OCFile mFile; - private Account mAccount; - private FileDataStorageManager mStorageManager; - - private UploadFinishReceiver mUploadFinishReceiver; - public ProgressListener mProgressListener; - - private Handler mHandler; - private RemoteOperation mLastRemoteOperation; - - private static final String TAG = FileDetailFragment.class.getSimpleName(); - public static final String FTAG = "FileDetails"; - public static final String FTAG_CONFIRMATION = "REMOVE_CONFIRMATION_FRAGMENT"; - - - /** - * Creates an empty details fragment. - * - * It's necessary to keep a public constructor without parameters; the system uses it when tries to reinstantiate a fragment automatically. - */ - public FileDetailFragment() { - mFile = null; - mAccount = null; - mStorageManager = null; - mLayout = R.layout.file_details_empty; - mProgressListener = null; - } - - - /** - * Creates a details fragment. - * - * When 'fileToDetail' or 'ocAccount' are null, creates a dummy layout (to use when a file wasn't tapped before). - * - * @param fileToDetail An {@link OCFile} to show in the fragment - * @param ocAccount An ownCloud account; needed to start downloads - */ - public FileDetailFragment(OCFile fileToDetail, Account ocAccount) { - mFile = fileToDetail; - mAccount = ocAccount; - mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment - mLayout = R.layout.file_details_empty; - mProgressListener = null; - } - - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mHandler = new Handler(); - } - - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - super.onCreateView(inflater, container, savedInstanceState); - - if (savedInstanceState != null) { - mFile = savedInstanceState.getParcelable(FileDetailFragment.EXTRA_FILE); - mAccount = savedInstanceState.getParcelable(FileDetailFragment.EXTRA_ACCOUNT); - } - - if(mFile != null && mAccount != null) { - mLayout = R.layout.file_details_fragment; - } - - View view = null; - view = inflater.inflate(mLayout, container, false); - mView = view; - - if (mLayout == R.layout.file_details_fragment) { - mView.findViewById(R.id.fdKeepInSync).setOnClickListener(this); - mView.findViewById(R.id.fdRenameBtn).setOnClickListener(this); - mView.findViewById(R.id.fdDownloadBtn).setOnClickListener(this); - mView.findViewById(R.id.fdOpenBtn).setOnClickListener(this); - mView.findViewById(R.id.fdRemoveBtn).setOnClickListener(this); - //mView.findViewById(R.id.fdShareBtn).setOnClickListener(this); - ProgressBar progressBar = (ProgressBar)mView.findViewById(R.id.fdProgressBar); - mProgressListener = new ProgressListener(progressBar); - } - - updateFileDetails(false, false); - return view; - } - - - /** - * {@inheritDoc} - */ - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - try { - mContainerActivity = (ContainerActivity) activity; - - } catch (ClassCastException e) { - throw new ClassCastException(activity.toString() + " must implement " + FileDetailFragment.ContainerActivity.class.getSimpleName()); - } - } - - - /** - * {@inheritDoc} - */ - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - if (mAccount != null) { - mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver()); - } - } - - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putParcelable(FileDetailFragment.EXTRA_FILE, mFile); - outState.putParcelable(FileDetailFragment.EXTRA_ACCOUNT, mAccount); - } - - @Override - public void onStart() { - super.onStart(); - listenForTransferProgress(); - } - - @Override - public void onResume() { - super.onResume(); - mUploadFinishReceiver = new UploadFinishReceiver(); - IntentFilter filter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE); - getActivity().registerReceiver(mUploadFinishReceiver, filter); - - } - - - @Override - public void onPause() { - super.onPause(); - if (mUploadFinishReceiver != null) { - getActivity().unregisterReceiver(mUploadFinishReceiver); - mUploadFinishReceiver = null; - } - } - - - @Override - public void onStop() { - super.onStop(); - leaveTransferProgress(); - } - - - @Override - public View getView() { - return super.getView() == null ? mView : super.getView(); - } - - - @Override - public void onClick(View v) { - switch (v.getId()) { - case R.id.fdDownloadBtn: { - FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder(); - FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder(); - if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile)) { - downloaderBinder.cancel(mAccount, mFile); - if (mFile.isDown()) { - setButtonsForDown(); - } else { - setButtonsForRemote(); - } - - } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile)) { - uploaderBinder.cancel(mAccount, mFile); - if (!mFile.fileExists()) { - // TODO make something better - if (getActivity() instanceof FileDisplayActivity) { - // double pane - FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null), FTAG); // empty FileDetailFragment - transaction.commit(); - mContainerActivity.onFileStateChanged(); - } else { - getActivity().finish(); - } - - } else if (mFile.isDown()) { - setButtonsForDown(); - } else { - setButtonsForRemote(); - } - - } else { - mLastRemoteOperation = new SynchronizeFileOperation(mFile, null, mStorageManager, mAccount, true, false, getActivity()); - WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getSherlockActivity().getApplicationContext()); - mLastRemoteOperation.execute(wc, this, mHandler); - - // update ui - boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity; - getActivity().showDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT); - - } - break; - } - case R.id.fdKeepInSync: { - CheckBox cb = (CheckBox) getView().findViewById(R.id.fdKeepInSync); - mFile.setKeepInSync(cb.isChecked()); - mStorageManager.saveFile(mFile); - - /// register the OCFile instance in the observer service to monitor local updates; - /// if necessary, the file is download - Intent intent = new Intent(getActivity().getApplicationContext(), - FileObserverService.class); - intent.putExtra(FileObserverService.KEY_FILE_CMD, - (cb.isChecked()? - FileObserverService.CMD_ADD_OBSERVED_FILE: - FileObserverService.CMD_DEL_OBSERVED_FILE)); - intent.putExtra(FileObserverService.KEY_CMD_ARG_FILE, mFile); - intent.putExtra(FileObserverService.KEY_CMD_ARG_ACCOUNT, mAccount); - getActivity().startService(intent); - - if (mFile.keepInSync()) { - onClick(getView().findViewById(R.id.fdDownloadBtn)); // force an immediate synchronization - } - break; - } - case R.id.fdRenameBtn: { - EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), mFile.getFileName(), this); - dialog.show(getFragmentManager(), "nameeditdialog"); - break; - } - case R.id.fdRemoveBtn: { - ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance( - R.string.confirmation_remove_alert, - new String[]{mFile.getFileName()}, - mFile.isDown() ? R.string.confirmation_remove_remote_and_local : R.string.confirmation_remove_remote, - mFile.isDown() ? R.string.confirmation_remove_local : -1, - R.string.common_cancel); - confDialog.setOnConfirmationListener(this); - confDialog.show(getFragmentManager(), FTAG_CONFIRMATION); - break; - } - case R.id.fdOpenBtn: { - openFile(); - break; - } - default: - Log.e(TAG, "Incorrect view clicked!"); - } - - /* else if (v.getId() == R.id.fdShareBtn) { - Thread t = new Thread(new ShareRunnable(mFile.getRemotePath())); - t.start(); - }*/ - } - - - /** - * Opens mFile. - */ - private void openFile() { - - String storagePath = mFile.getStoragePath(); - String encodedStoragePath = WebdavUtils.encodePath(storagePath); - try { - Intent i = new Intent(Intent.ACTION_VIEW); - i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mFile.getMimetype()); - i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - startActivity(i); - - } catch (Throwable t) { - Log.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + mFile.getMimetype()); - boolean toastIt = true; - String mimeType = ""; - try { - Intent i = new Intent(Intent.ACTION_VIEW); - mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1)); - if (mimeType == null || !mimeType.equals(mFile.getMimetype())) { - if (mimeType != null) { - i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mimeType); - } else { - // desperate try - i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), "*/*"); - } - i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - startActivity(i); - toastIt = false; - } - - } catch (IndexOutOfBoundsException e) { - Log.e(TAG, "Trying to find out MIME type of a file without extension: " + storagePath); - - } catch (ActivityNotFoundException e) { - Log.e(TAG, "No activity found to handle: " + storagePath + " with MIME type " + mimeType + " obtained from extension"); - - } catch (Throwable th) { - Log.e(TAG, "Unexpected problem when opening: " + storagePath, th); - - } finally { - if (toastIt) { - Toast.makeText(getActivity(), "There is no application to handle file " + mFile.getFileName(), Toast.LENGTH_SHORT).show(); - } - } - - } - } - - - @Override - public void onConfirmation(String callerTag) { - if (callerTag.equals(FTAG_CONFIRMATION)) { - if (mStorageManager.getFileById(mFile.getFileId()) != null) { - mLastRemoteOperation = new RemoveFileOperation( mFile, - true, - mStorageManager); - WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getSherlockActivity().getApplicationContext()); - mLastRemoteOperation.execute(wc, this, mHandler); - - boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity; - getActivity().showDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT); - } - } - } - - @Override - public void onNeutral(String callerTag) { - File f = null; - if (mFile.isDown() && (f = new File(mFile.getStoragePath())).exists()) { - f.delete(); - mFile.setStoragePath(null); - mStorageManager.saveFile(mFile); - updateFileDetails(mFile, mAccount); - } - } - - @Override - public void onCancel(String callerTag) { - Log.d(TAG, "REMOVAL CANCELED"); - } - - - /** - * Check if the fragment was created with an empty layout. An empty fragment can't show file details, must be replaced. - * - * @return True when the fragment was created with the empty layout. - */ - public boolean isEmpty() { - return (mLayout == R.layout.file_details_empty || mFile == null || mAccount == null); - } - - - /** - * {@inheritDoc} - */ - public OCFile getFile(){ - return mFile; - } - - /** - * Use this method to signal this Activity that it shall update its view. - * - * @param file : An {@link OCFile} - */ - public void updateFileDetails(OCFile file, Account ocAccount) { - mFile = file; - if (ocAccount != null && ( - mStorageManager == null || - (mAccount != null && !mAccount.equals(ocAccount)) - )) { - mStorageManager = new FileDataStorageManager(ocAccount, getActivity().getApplicationContext().getContentResolver()); - } - mAccount = ocAccount; - updateFileDetails(false, false); - } - - - /** - * Updates the view with all relevant details about that file. - * - * TODO Remove parameter when the transferring state of files is kept in database. - * - * TODO REFACTORING! this method called 5 times before every time the fragment is shown! - * - * @param transferring Flag signaling if the file should be considered as downloading or uploading, - * although {@link FileDownloaderBinder#isDownloading(Account, OCFile)} and - * {@link FileUploaderBinder#isUploading(Account, OCFile)} return false. - * - * @param refresh If 'true', try to refresh the hold file from the database - */ - public void updateFileDetails(boolean transferring, boolean refresh) { - - if (readyToShow()) { - - if (refresh && mStorageManager != null) { - mFile = mStorageManager.getFileByPath(mFile.getRemotePath()); - } - - // set file details - setFilename(mFile.getFileName()); - setFiletype(mFile.getMimetype()); - setFilesize(mFile.getFileLength()); - if(ocVersionSupportsTimeCreated()){ - setTimeCreated(mFile.getCreationTimestamp()); - } - - setTimeModified(mFile.getModificationTimestamp()); - - CheckBox cb = (CheckBox)getView().findViewById(R.id.fdKeepInSync); - cb.setChecked(mFile.keepInSync()); - - // configure UI for depending upon local state of the file - //if (FileDownloader.isDownloading(mAccount, mFile.getRemotePath()) || FileUploader.isUploading(mAccount, mFile.getRemotePath())) { - FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder(); - FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder(); - if (transferring || (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile)) || (uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile))) { - setButtonsForTransferring(); - - } else if (mFile.isDown()) { - - setButtonsForDown(); - - } else { - // TODO load default preview image; when the local file is removed, the preview remains there - setButtonsForRemote(); - } - } - getView().invalidate(); - } - - - /** - * Checks if the fragment is ready to show details of a OCFile - * - * @return 'True' when the fragment is ready to show details of a file - */ - private boolean readyToShow() { - return (mFile != null && mAccount != null && mLayout == R.layout.file_details_fragment); - } - - - - /** - * Updates the filename in view - * @param filename to set - */ - private void setFilename(String filename) { - TextView tv = (TextView) getView().findViewById(R.id.fdFilename); - if (tv != null) - tv.setText(filename); - } - - /** - * Updates the MIME type in view - * @param mimetype to set - */ - private void setFiletype(String mimetype) { - TextView tv = (TextView) getView().findViewById(R.id.fdType); - if (tv != null) { - String printableMimetype = DisplayUtils.convertMIMEtoPrettyPrint(mimetype);; - tv.setText(printableMimetype); - } - ImageView iv = (ImageView) getView().findViewById(R.id.fdIcon); - if (iv != null) { - iv.setImageResource(DisplayUtils.getResourceId(mimetype)); - } - } - - /** - * Updates the file size in view - * @param filesize in bytes to set - */ - private void setFilesize(long filesize) { - TextView tv = (TextView) getView().findViewById(R.id.fdSize); - if (tv != null) - tv.setText(DisplayUtils.bytesToHumanReadable(filesize)); - } - - /** - * Updates the time that the file was created in view - * @param milliseconds Unix time to set - */ - private void setTimeCreated(long milliseconds){ - TextView tv = (TextView) getView().findViewById(R.id.fdCreated); - TextView tvLabel = (TextView) getView().findViewById(R.id.fdCreatedLabel); - if(tv != null){ - tv.setText(DisplayUtils.unixTimeToHumanReadable(milliseconds)); - tv.setVisibility(View.VISIBLE); - tvLabel.setVisibility(View.VISIBLE); - } - } - - /** - * Updates the time that the file was last modified - * @param milliseconds Unix time to set - */ - private void setTimeModified(long milliseconds){ - TextView tv = (TextView) getView().findViewById(R.id.fdModified); - if(tv != null){ - tv.setText(DisplayUtils.unixTimeToHumanReadable(milliseconds)); - } - } - - /** - * Enables or disables buttons for a file being downloaded - */ - private void setButtonsForTransferring() { - if (!isEmpty()) { - Button downloadButton = (Button) getView().findViewById(R.id.fdDownloadBtn); - downloadButton.setText(R.string.common_cancel); - //downloadButton.setEnabled(false); - - // let's protect the user from himself ;) - ((Button) getView().findViewById(R.id.fdOpenBtn)).setEnabled(false); - ((Button) getView().findViewById(R.id.fdRenameBtn)).setEnabled(false); - ((Button) getView().findViewById(R.id.fdRemoveBtn)).setEnabled(false); - getView().findViewById(R.id.fdKeepInSync).setEnabled(false); - - // show the progress bar for the transfer - ProgressBar progressBar = (ProgressBar)getView().findViewById(R.id.fdProgressBar); - progressBar.setVisibility(View.VISIBLE); - TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText); - progressText.setVisibility(View.VISIBLE); - FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder(); - FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder(); - if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile)) { - progressText.setText(R.string.downloader_download_in_progress_ticker); - } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile)) { - progressText.setText(R.string.uploader_upload_in_progress_ticker); - } - } - } - - - /** - * Enables or disables buttons for a file locally available - */ - private void setButtonsForDown() { - if (!isEmpty()) { - Button downloadButton = (Button) getView().findViewById(R.id.fdDownloadBtn); - downloadButton.setText(R.string.filedetails_sync_file); - - ((Button) getView().findViewById(R.id.fdOpenBtn)).setEnabled(true); - ((Button) getView().findViewById(R.id.fdRenameBtn)).setEnabled(true); - ((Button) getView().findViewById(R.id.fdRemoveBtn)).setEnabled(true); - getView().findViewById(R.id.fdKeepInSync).setEnabled(true); - - // hides the progress bar - ProgressBar progressBar = (ProgressBar)getView().findViewById(R.id.fdProgressBar); - progressBar.setVisibility(View.GONE); - TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText); - progressText.setVisibility(View.GONE); - } - } - - /** - * Enables or disables buttons for a file not locally available - */ - private void setButtonsForRemote() { - if (!isEmpty()) { - Button downloadButton = (Button) getView().findViewById(R.id.fdDownloadBtn); - downloadButton.setText(R.string.filedetails_download); - - ((Button) getView().findViewById(R.id.fdOpenBtn)).setEnabled(false); - ((Button) getView().findViewById(R.id.fdRenameBtn)).setEnabled(true); - ((Button) getView().findViewById(R.id.fdRemoveBtn)).setEnabled(true); - getView().findViewById(R.id.fdKeepInSync).setEnabled(true); - - // hides the progress bar - ProgressBar progressBar = (ProgressBar)getView().findViewById(R.id.fdProgressBar); - progressBar.setVisibility(View.GONE); - TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText); - progressText.setVisibility(View.GONE); - } - } - - - /** - * In ownCloud 3.X.X and 4.X.X there is a bug that SabreDAV does not return - * the time that the file was created. There is a chance that this will - * be fixed in future versions. Use this method to check if this version of - * ownCloud has this fix. - * @return True, if ownCloud the ownCloud version is supporting creation time - */ - private boolean ocVersionSupportsTimeCreated(){ - /*if(mAccount != null){ - AccountManager accManager = (AccountManager) getActivity().getSystemService(Context.ACCOUNT_SERVICE); - OwnCloudVersion ocVersion = new OwnCloudVersion(accManager - .getUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION)); - if(ocVersion.compareTo(new OwnCloudVersion(0x030000)) < 0) { - return true; - } - }*/ - return false; - } - - - /** - * Once the file upload has finished -> update view - * - * Being notified about the finish of an upload is necessary for the next sequence: - * 1. Upload a big file. - * 2. Force a synchronization; if it finished before the upload, the file in transfer will be included in the local database and in the file list - * of its containing folder; the the server includes it in the PROPFIND requests although it's not fully upload. - * 3. Click the file in the list to see its details. - * 4. Wait for the upload finishes; at this moment, the details view must be refreshed to enable the action buttons. - */ - private class UploadFinishReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - String accountName = intent.getStringExtra(FileUploader.ACCOUNT_NAME); - - if (!isEmpty() && accountName.equals(mAccount.name)) { - boolean uploadWasFine = intent.getBooleanExtra(FileUploader.EXTRA_UPLOAD_RESULT, false); - String uploadRemotePath = intent.getStringExtra(FileUploader.EXTRA_REMOTE_PATH); - boolean renamedInUpload = mFile.getRemotePath().equals(intent.getStringExtra(FileUploader.EXTRA_OLD_REMOTE_PATH)); - if (mFile.getRemotePath().equals(uploadRemotePath) || - renamedInUpload) { - if (uploadWasFine) { - mFile = mStorageManager.getFileByPath(uploadRemotePath); - } - if (renamedInUpload) { - String newName = (new File(uploadRemotePath)).getName(); - Toast msg = Toast.makeText(getActivity().getApplicationContext(), String.format(getString(R.string.filedetails_renamed_in_upload_msg), newName), Toast.LENGTH_LONG); - msg.show(); - } - getSherlockActivity().removeStickyBroadcast(intent); // not the best place to do this; a small refactorization of BroadcastReceivers should be done - updateFileDetails(false, false); // it updates the buttons; must be called although !uploadWasFine; interrupted uploads still leave an incomplete file in the server - } - } - } - } - - - // this is a temporary class for sharing purposes, it need to be replaced in transfer service - @SuppressWarnings("unused") - private class ShareRunnable implements Runnable { - private String mPath; - - public ShareRunnable(String path) { - mPath = path; - } - - public void run() { - AccountManager am = AccountManager.get(getActivity()); - Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity()); - OwnCloudVersion ocv = new OwnCloudVersion(am.getUserData(account, AccountAuthenticator.KEY_OC_VERSION)); - String url = am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL) + AccountUtils.getWebdavPath(ocv); - - Log.d("share", "sharing for version " + ocv.toString()); - - if (ocv.compareTo(new OwnCloudVersion(0x040000)) >= 0) { - String APPS_PATH = "/apps/files_sharing/"; - String SHARE_PATH = "ajax/share.php"; - - String SHARED_PATH = "/apps/files_sharing/get.php?token="; - - final String WEBDAV_SCRIPT = "webdav.php"; - final String WEBDAV_FILES_LOCATION = "/files/"; - - WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(account, getActivity().getApplicationContext()); - HttpConnectionManagerParams params = new HttpConnectionManagerParams(); - params.setMaxConnectionsPerHost(wc.getHostConfiguration(), 5); - - //wc.getParams().setParameter("http.protocol.single-cookie-header", true); - //wc.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY); - - PostMethod post = new PostMethod(am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL) + APPS_PATH + SHARE_PATH); - - post.addRequestHeader("Content-type","application/x-www-form-urlencoded; charset=UTF-8" ); - post.addRequestHeader("Referer", am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL)); - List formparams = new ArrayList(); - Log.d("share", mPath+""); - formparams.add(new BasicNameValuePair("sources",mPath)); - formparams.add(new BasicNameValuePair("uid_shared_with", "public")); - formparams.add(new BasicNameValuePair("permissions", "0")); - post.setRequestEntity(new StringRequestEntity(URLEncodedUtils.format(formparams, HTTP.UTF_8))); - - int status; - try { - PropFindMethod find = new PropFindMethod(url+"/"); - find.addRequestHeader("Referer", am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL)); - Log.d("sharer", ""+ url+"/"); - - for (org.apache.commons.httpclient.Header a : find.getRequestHeaders()) { - Log.d("sharer-h", a.getName() + ":"+a.getValue()); - } - - int status2 = wc.executeMethod(find); - - Log.d("sharer", "propstatus "+status2); - - GetMethod get = new GetMethod(am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL) + "/"); - get.addRequestHeader("Referer", am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL)); - - status2 = wc.executeMethod(get); - - Log.d("sharer", "getstatus "+status2); - Log.d("sharer", "" + get.getResponseBodyAsString()); - - for (org.apache.commons.httpclient.Header a : get.getResponseHeaders()) { - Log.d("sharer", a.getName() + ":"+a.getValue()); - } - - status = wc.executeMethod(post); - for (org.apache.commons.httpclient.Header a : post.getRequestHeaders()) { - Log.d("sharer-h", a.getName() + ":"+a.getValue()); - } - for (org.apache.commons.httpclient.Header a : post.getResponseHeaders()) { - Log.d("sharer", a.getName() + ":"+a.getValue()); - } - String resp = post.getResponseBodyAsString(); - Log.d("share", ""+post.getURI().toString()); - Log.d("share", "returned status " + status); - Log.d("share", " " +resp); - - if(status != HttpStatus.SC_OK ||resp == null || resp.equals("") || resp.startsWith("false")) { - return; - } - - JSONObject jsonObject = new JSONObject (resp); - String jsonStatus = jsonObject.getString("status"); - if(!jsonStatus.equals("success")) throw new Exception("Error while sharing file status != success"); - - String token = jsonObject.getString("data"); - String uri = am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL) + SHARED_PATH + token; - Log.d("Actions:shareFile ok", "url: " + uri); - - } catch (Exception e) { - e.printStackTrace(); - } - - } else if (ocv.compareTo(new OwnCloudVersion(0x030000)) >= 0) { - - } - } - } - - public void onDismiss(EditNameDialog dialog) { - if (dialog.getResult()) { - String newFilename = dialog.getNewFilename(); - Log.d(TAG, "name edit dialog dismissed with new name " + newFilename); - mLastRemoteOperation = new RenameFileOperation( mFile, - mAccount, - newFilename, - new FileDataStorageManager(mAccount, getActivity().getContentResolver())); - WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getSherlockActivity().getApplicationContext()); - mLastRemoteOperation.execute(wc, this, mHandler); - boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity; - getActivity().showDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT); - } - } - - - /** - * {@inheritDoc} - */ - @Override - public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { - if (operation.equals(mLastRemoteOperation)) { - if (operation instanceof RemoveFileOperation) { - onRemoveFileOperationFinish((RemoveFileOperation)operation, result); - - } else if (operation instanceof RenameFileOperation) { - onRenameFileOperationFinish((RenameFileOperation)operation, result); - - } else if (operation instanceof SynchronizeFileOperation) { - onSynchronizeFileOperationFinish((SynchronizeFileOperation)operation, result); - } - } - } - - - private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) { - boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity; - getActivity().dismissDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT); - - if (result.isSuccess()) { - Toast msg = Toast.makeText(getActivity().getApplicationContext(), R.string.remove_success_msg, Toast.LENGTH_LONG); - msg.show(); - if (inDisplayActivity) { - // double pane - FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null)); // empty FileDetailFragment - transaction.commit(); - mContainerActivity.onFileStateChanged(); - } else { - getActivity().finish(); - } - - } else { - Toast msg = Toast.makeText(getActivity(), R.string.remove_fail_msg, Toast.LENGTH_LONG); - msg.show(); - if (result.isSslRecoverableException()) { - // TODO show the SSL warning dialog - } - } - } - - private void onRenameFileOperationFinish(RenameFileOperation operation, RemoteOperationResult result) { - boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity; - getActivity().dismissDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT); - - if (result.isSuccess()) { - updateFileDetails(((RenameFileOperation)operation).getFile(), mAccount); - mContainerActivity.onFileStateChanged(); - - } else { - if (result.getCode().equals(ResultCode.INVALID_LOCAL_FILE_NAME)) { - Toast msg = Toast.makeText(getActivity(), R.string.rename_local_fail_msg, Toast.LENGTH_LONG); - msg.show(); - // TODO throw again the new rename dialog - } else { - Toast msg = Toast.makeText(getActivity(), R.string.rename_server_fail_msg, Toast.LENGTH_LONG); - msg.show(); - if (result.isSslRecoverableException()) { - // TODO show the SSL warning dialog - } - } - } - } - - private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation, RemoteOperationResult result) { - boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity; - getActivity().dismissDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT); - - if (!result.isSuccess()) { - if (result.getCode() == ResultCode.SYNC_CONFLICT) { - Intent i = new Intent(getActivity(), ConflictsResolveActivity.class); - i.putExtra(ConflictsResolveActivity.EXTRA_FILE, mFile); - i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, mAccount); - startActivity(i); - - } else { - Toast msg = Toast.makeText(getActivity(), R.string.sync_file_fail_msg, Toast.LENGTH_LONG); - msg.show(); - } - - if (mFile.isDown()) { - setButtonsForDown(); - - } else { - setButtonsForRemote(); - } - - } else { - if (operation.transferWasRequested()) { - setButtonsForTransferring(); - mContainerActivity.onFileStateChanged(); // this is not working; FileDownloader won't do NOTHING at all until this method finishes, so - // checking the service to see if the file is downloading results in FALSE - } else { - Toast msg = Toast.makeText(getActivity(), R.string.sync_file_nothing_to_do_msg, Toast.LENGTH_LONG); - msg.show(); - if (mFile.isDown()) { - setButtonsForDown(); - - } else { - setButtonsForRemote(); - } - } - } - } - - - public void listenForTransferProgress() { - if (mProgressListener != null) { - if (mContainerActivity.getFileDownloaderBinder() != null) { - mContainerActivity.getFileDownloaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, mFile); - } - if (mContainerActivity.getFileUploaderBinder() != null) { - mContainerActivity.getFileUploaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, mFile); - } - } - } - - - public void leaveTransferProgress() { - if (mProgressListener != null) { - if (mContainerActivity.getFileDownloaderBinder() != null) { - mContainerActivity.getFileDownloaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, mFile); - } - if (mContainerActivity.getFileUploaderBinder() != null) { - mContainerActivity.getFileUploaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, mFile); - } - } - } - - - - /** - * Helper class responsible for updating the progress bar shown for file uploading or downloading - * - * @author David A. Velasco - */ - private class ProgressListener implements OnDatatransferProgressListener { - int mLastPercent = 0; - WeakReference mProgressBar = null; - - ProgressListener(ProgressBar progressBar) { - mProgressBar = new WeakReference(progressBar); - } - - @Override - public void onTransferProgress(long progressRate) { - // old method, nothing here - }; - - @Override - public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filename) { - int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer)); - if (percent != mLastPercent) { - ProgressBar pb = mProgressBar.get(); - if (pb != null) { - pb.setProgress(percent); - pb.postInvalidate(); - } - } - mLastPercent = percent; - } - - }; - - - -} +/* ownCloud Android client application + * Copyright (C) 2011 Bartek Przybylski + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.owncloud.android.ui.fragment; + +import java.io.File; ++import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.commons.httpclient.methods.StringRequestEntity; +import org.apache.commons.httpclient.params.HttpConnectionManagerParams; +import org.apache.http.HttpStatus; +import org.apache.http.NameValuePair; +import org.apache.http.client.utils.URLEncodedUtils; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.protocol.HTTP; +import org.apache.jackrabbit.webdav.client.methods.PropFindMethod; +import org.json.JSONObject; + +import android.accounts.Account; +import android.accounts.AccountManager; - import android.annotation.SuppressLint; ++//import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.ActivityNotFoundException; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; ++import android.net.Uri; ++import android.os.Bundle; ++import android.os.Handler; ++import android.support.v4.app.FragmentTransaction; ++import android.util.Log; ++import android.view.LayoutInflater; ++import android.view.View; ++import android.view.View.OnClickListener; ++import android.view.ViewGroup; ++import android.webkit.MimeTypeMap; ++import android.widget.Button; ++import android.widget.CheckBox; ++import android.widget.ImageView; ++import android.widget.ProgressBar; ++import android.widget.TextView; ++import android.widget.Toast; ++ +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.BitmapFactory.Options; +import android.graphics.Point; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Handler; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentTransaction; +import android.util.Log; +import android.view.Display; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.webkit.MimeTypeMap; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + ++ +import com.actionbarsherlock.app.SherlockFragment; +import com.owncloud.android.AccountUtils; +import com.owncloud.android.DisplayUtils; +import com.owncloud.android.Log_OC; +import com.owncloud.android.authenticator.AccountAuthenticator; +import com.owncloud.android.datamodel.FileDataStorageManager; +import com.owncloud.android.datamodel.OCFile; - import com.owncloud.android.files.services.FileDownloader; +import com.owncloud.android.files.services.FileObserverService; +import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; +import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; +import com.owncloud.android.network.OwnCloudClientUtils; +import com.owncloud.android.operations.OnRemoteOperationListener; +import com.owncloud.android.operations.RemoteOperation; +import com.owncloud.android.operations.RemoteOperationResult; +import com.owncloud.android.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.operations.RemoveFileOperation; +import com.owncloud.android.operations.RenameFileOperation; +import com.owncloud.android.operations.SynchronizeFileOperation; +import com.owncloud.android.ui.activity.ConflictsResolveActivity; +import com.owncloud.android.ui.activity.FileDetailActivity; +import com.owncloud.android.ui.activity.FileDisplayActivity; - import com.owncloud.android.ui.activity.TransferServiceGetter; +import com.owncloud.android.ui.dialog.EditNameDialog; +import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener; +import com.owncloud.android.utils.OwnCloudVersion; + +import com.owncloud.android.R; ++ ++import eu.alefzero.webdav.OnDatatransferProgressListener; +import eu.alefzero.webdav.WebdavClient; +import eu.alefzero.webdav.WebdavUtils; + ++ +/** + * This Fragment is used to display the details about a file. + * + * @author Bartek Przybylski - * ++ * @author David A. Velasco + */ +public class FileDetailFragment extends SherlockFragment implements - OnClickListener, ConfirmationDialogFragment.ConfirmationDialogFragmentListener, OnRemoteOperationListener, EditNameDialogListener { ++ OnClickListener, ++ ConfirmationDialogFragment.ConfirmationDialogFragmentListener, OnRemoteOperationListener, EditNameDialogListener, ++ FileFragment { + + public static final String EXTRA_FILE = "FILE"; + public static final String EXTRA_ACCOUNT = "ACCOUNT"; + - private FileDetailFragment.ContainerActivity mContainerActivity; ++ private FileFragment.ContainerActivity mContainerActivity; + + private int mLayout; + private View mView; + private OCFile mFile; + private Account mAccount; + private FileDataStorageManager mStorageManager; - private ImageView mPreview; + - private DownloadFinishReceiver mDownloadFinishReceiver; + private UploadFinishReceiver mUploadFinishReceiver; ++ public ProgressListener mProgressListener; + + private Handler mHandler; + private RemoteOperation mLastRemoteOperation; - private DialogFragment mCurrentDialog; - ++ + private static final String TAG = FileDetailFragment.class.getSimpleName(); + public static final String FTAG = "FileDetails"; + public static final String FTAG_CONFIRMATION = "REMOVE_CONFIRMATION_FRAGMENT"; - + ++ + /** + * Creates an empty details fragment. + * + * It's necessary to keep a public constructor without parameters; the system uses it when tries to reinstantiate a fragment automatically. + */ + public FileDetailFragment() { + mFile = null; + mAccount = null; + mStorageManager = null; + mLayout = R.layout.file_details_empty; ++ mProgressListener = null; + } + + + /** + * Creates a details fragment. + * + * When 'fileToDetail' or 'ocAccount' are null, creates a dummy layout (to use when a file wasn't tapped before). + * + * @param fileToDetail An {@link OCFile} to show in the fragment + * @param ocAccount An ownCloud account; needed to start downloads + */ + public FileDetailFragment(OCFile fileToDetail, Account ocAccount) { + mFile = fileToDetail; + mAccount = ocAccount; + mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment + mLayout = R.layout.file_details_empty; ++ mProgressListener = null; + } + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mHandler = new Handler(); + } + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + + if (savedInstanceState != null) { + mFile = savedInstanceState.getParcelable(FileDetailFragment.EXTRA_FILE); + mAccount = savedInstanceState.getParcelable(FileDetailFragment.EXTRA_ACCOUNT); + } + + if(mFile != null && mAccount != null) { + mLayout = R.layout.file_details_fragment; + } + + View view = null; + view = inflater.inflate(mLayout, container, false); + mView = view; + + if (mLayout == R.layout.file_details_fragment) { + mView.findViewById(R.id.fdKeepInSync).setOnClickListener(this); + mView.findViewById(R.id.fdRenameBtn).setOnClickListener(this); + mView.findViewById(R.id.fdDownloadBtn).setOnClickListener(this); + mView.findViewById(R.id.fdOpenBtn).setOnClickListener(this); + mView.findViewById(R.id.fdRemoveBtn).setOnClickListener(this); + //mView.findViewById(R.id.fdShareBtn).setOnClickListener(this); - mPreview = (ImageView)mView.findViewById(R.id.fdPreview); ++ ProgressBar progressBar = (ProgressBar)mView.findViewById(R.id.fdProgressBar); ++ mProgressListener = new ProgressListener(progressBar); + } + - updateFileDetails(false); ++ updateFileDetails(false, false); + return view; + } + + + /** + * {@inheritDoc} + */ + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + try { + mContainerActivity = (ContainerActivity) activity; ++ + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + " must implement " + FileDetailFragment.ContainerActivity.class.getSimpleName()); + } + } + + + /** + * {@inheritDoc} + */ + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + if (mAccount != null) { - mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());; ++ mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver()); + } + } + + + @Override + public void onSaveInstanceState(Bundle outState) { - Log_OC.i(getClass().toString(), "onSaveInstanceState() start"); + super.onSaveInstanceState(outState); + outState.putParcelable(FileDetailFragment.EXTRA_FILE, mFile); + outState.putParcelable(FileDetailFragment.EXTRA_ACCOUNT, mAccount); - Log_OC.i(getClass().toString(), "onSaveInstanceState() end"); + } + ++ @Override ++ public void onStart() { ++ super.onStart(); ++ listenForTransferProgress(); ++ } + + @Override + public void onResume() { + super.onResume(); - - mDownloadFinishReceiver = new DownloadFinishReceiver(); - IntentFilter filter = new IntentFilter( - FileDownloader.DOWNLOAD_FINISH_MESSAGE); - getActivity().registerReceiver(mDownloadFinishReceiver, filter); - + mUploadFinishReceiver = new UploadFinishReceiver(); - filter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE); ++ IntentFilter filter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE); + getActivity().registerReceiver(mUploadFinishReceiver, filter); - - mPreview = (ImageView)mView.findViewById(R.id.fdPreview); ++ + } + ++ + @Override + public void onPause() { + super.onPause(); - - getActivity().unregisterReceiver(mDownloadFinishReceiver); - mDownloadFinishReceiver = null; - - getActivity().unregisterReceiver(mUploadFinishReceiver); - mUploadFinishReceiver = null; - - if (mPreview != null) { - mPreview = null; ++ if (mUploadFinishReceiver != null) { ++ getActivity().unregisterReceiver(mUploadFinishReceiver); ++ mUploadFinishReceiver = null; + } + } + ++ ++ @Override ++ public void onStop() { ++ super.onStop(); ++ leaveTransferProgress(); ++ } ++ ++ + @Override + public View getView() { + return super.getView() == null ? mView : super.getView(); + } + - - + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.fdDownloadBtn: { + FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder(); + FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder(); + if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile)) { + downloaderBinder.cancel(mAccount, mFile); + if (mFile.isDown()) { + setButtonsForDown(); + } else { + setButtonsForRemote(); + } + + } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile)) { + uploaderBinder.cancel(mAccount, mFile); + if (!mFile.fileExists()) { + // TODO make something better + if (getActivity() instanceof FileDisplayActivity) { + // double pane + FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction(); + transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null), FTAG); // empty FileDetailFragment + transaction.commit(); + mContainerActivity.onFileStateChanged(); + } else { + getActivity().finish(); + } + + } else if (mFile.isDown()) { + setButtonsForDown(); + } else { + setButtonsForRemote(); + } + + } else { + mLastRemoteOperation = new SynchronizeFileOperation(mFile, null, mStorageManager, mAccount, true, false, getActivity()); + WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getSherlockActivity().getApplicationContext()); + mLastRemoteOperation.execute(wc, this, mHandler); + + // update ui + boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity; + getActivity().showDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT); - setButtonsForTransferring(); // disable button immediately, although the synchronization does not result in a file transference + + } + break; + } + case R.id.fdKeepInSync: { + CheckBox cb = (CheckBox) getView().findViewById(R.id.fdKeepInSync); + mFile.setKeepInSync(cb.isChecked()); + mStorageManager.saveFile(mFile); + + /// register the OCFile instance in the observer service to monitor local updates; + /// if necessary, the file is download + Intent intent = new Intent(getActivity().getApplicationContext(), + FileObserverService.class); + intent.putExtra(FileObserverService.KEY_FILE_CMD, + (cb.isChecked()? + FileObserverService.CMD_ADD_OBSERVED_FILE: + FileObserverService.CMD_DEL_OBSERVED_FILE)); + intent.putExtra(FileObserverService.KEY_CMD_ARG_FILE, mFile); + intent.putExtra(FileObserverService.KEY_CMD_ARG_ACCOUNT, mAccount); - Log_OC.e(TAG, "starting observer service"); + getActivity().startService(intent); + + if (mFile.keepInSync()) { + onClick(getView().findViewById(R.id.fdDownloadBtn)); // force an immediate synchronization + } + break; + } + case R.id.fdRenameBtn: { + EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), mFile.getFileName(), this); + dialog.show(getFragmentManager(), "nameeditdialog"); + break; + } + case R.id.fdRemoveBtn: { + ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance( + R.string.confirmation_remove_alert, + new String[]{mFile.getFileName()}, + mFile.isDown() ? R.string.confirmation_remove_remote_and_local : R.string.confirmation_remove_remote, + mFile.isDown() ? R.string.confirmation_remove_local : -1, + R.string.common_cancel); + confDialog.setOnConfirmationListener(this); - mCurrentDialog = confDialog; - mCurrentDialog.show(getFragmentManager(), FTAG_CONFIRMATION); ++ confDialog.show(getFragmentManager(), FTAG_CONFIRMATION); + break; + } + case R.id.fdOpenBtn: { - String storagePath = mFile.getStoragePath(); - String encodedStoragePath = WebdavUtils.encodePath(storagePath); - try { - Intent i = new Intent(Intent.ACTION_VIEW); - i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mFile.getMimetype()); - i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - startActivity(i); - - } catch (Throwable t) { - Log_OC.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + mFile.getMimetype()); - boolean toastIt = true; - String mimeType = ""; - try { - Intent i = new Intent(Intent.ACTION_VIEW); - mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1)); - if (mimeType == null || !mimeType.equals(mFile.getMimetype())) { - if (mimeType != null) { - i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mimeType); - } else { - // desperate try - i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), "*/*"); - } - i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - startActivity(i); - toastIt = false; - } - - } catch (IndexOutOfBoundsException e) { - Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + storagePath); - - } catch (ActivityNotFoundException e) { - Log_OC.e(TAG, "No activity found to handle: " + storagePath + " with MIME type " + mimeType + " obtained from extension"); - - } catch (Throwable th) { - Log_OC.e(TAG, "Unexpected problem when opening: " + storagePath, th); - - } finally { - if (toastIt) { - Toast.makeText(getActivity(), "There is no application to handle file " + mFile.getFileName(), Toast.LENGTH_SHORT).show(); - } - } - - } ++ openFile(); + break; + } + default: + Log_OC.e(TAG, "Incorrect view clicked!"); + } + + /* else if (v.getId() == R.id.fdShareBtn) { + Thread t = new Thread(new ShareRunnable(mFile.getRemotePath())); + t.start(); + }*/ + } + + ++ /** ++ * Opens mFile. ++ */ ++ private void openFile() { ++ ++ String storagePath = mFile.getStoragePath(); ++ String encodedStoragePath = WebdavUtils.encodePath(storagePath); ++ try { ++ Intent i = new Intent(Intent.ACTION_VIEW); ++ i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mFile.getMimetype()); ++ i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); ++ startActivity(i); ++ ++ } catch (Throwable t) { ++ Log.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + mFile.getMimetype()); ++ boolean toastIt = true; ++ String mimeType = ""; ++ try { ++ Intent i = new Intent(Intent.ACTION_VIEW); ++ mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1)); ++ if (mimeType == null || !mimeType.equals(mFile.getMimetype())) { ++ if (mimeType != null) { ++ i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mimeType); ++ } else { ++ // desperate try ++ i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), "*/*"); ++ } ++ i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); ++ startActivity(i); ++ toastIt = false; ++ } ++ ++ } catch (IndexOutOfBoundsException e) { ++ Log.e(TAG, "Trying to find out MIME type of a file without extension: " + storagePath); ++ ++ } catch (ActivityNotFoundException e) { ++ Log.e(TAG, "No activity found to handle: " + storagePath + " with MIME type " + mimeType + " obtained from extension"); ++ ++ } catch (Throwable th) { ++ Log.e(TAG, "Unexpected problem when opening: " + storagePath, th); ++ ++ } finally { ++ if (toastIt) { ++ Toast.makeText(getActivity(), "There is no application to handle file " + mFile.getFileName(), Toast.LENGTH_SHORT).show(); ++ } ++ } ++ ++ } ++ } ++ ++ + @Override + public void onConfirmation(String callerTag) { + if (callerTag.equals(FTAG_CONFIRMATION)) { + if (mStorageManager.getFileById(mFile.getFileId()) != null) { + mLastRemoteOperation = new RemoveFileOperation( mFile, + true, + mStorageManager); + WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getSherlockActivity().getApplicationContext()); + mLastRemoteOperation.execute(wc, this, mHandler); + + boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity; + getActivity().showDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT); + } + } - mCurrentDialog.dismiss(); - mCurrentDialog = null; + } + + @Override + public void onNeutral(String callerTag) { + File f = null; + if (mFile.isDown() && (f = new File(mFile.getStoragePath())).exists()) { + f.delete(); + mFile.setStoragePath(null); + mStorageManager.saveFile(mFile); + updateFileDetails(mFile, mAccount); + } - mCurrentDialog.dismiss(); - mCurrentDialog = null; + } + + @Override + public void onCancel(String callerTag) { - Log_OC.d(TAG, "REMOVAL CANCELED"); - mCurrentDialog.dismiss(); - mCurrentDialog = null; ++ Log.d(TAG, "REMOVAL CANCELED"); + } + + + /** + * Check if the fragment was created with an empty layout. An empty fragment can't show file details, must be replaced. + * + * @return True when the fragment was created with the empty layout. + */ + public boolean isEmpty() { + return (mLayout == R.layout.file_details_empty || mFile == null || mAccount == null); + } + + + /** - * Can be used to get the file that is currently being displayed. - * @return The file on the screen. ++ * {@inheritDoc} + */ - public OCFile getDisplayedFile(){ ++ public OCFile getFile(){ + return mFile; + } + + /** + * Use this method to signal this Activity that it shall update its view. + * + * @param file : An {@link OCFile} + */ + public void updateFileDetails(OCFile file, Account ocAccount) { + mFile = file; + if (ocAccount != null && ( + mStorageManager == null || + (mAccount != null && !mAccount.equals(ocAccount)) + )) { + mStorageManager = new FileDataStorageManager(ocAccount, getActivity().getApplicationContext().getContentResolver()); + } + mAccount = ocAccount; - updateFileDetails(false); ++ updateFileDetails(false, false); + } - + + /** + * Updates the view with all relevant details about that file. + * + * TODO Remove parameter when the transferring state of files is kept in database. + * ++ * TODO REFACTORING! this method called 5 times before every time the fragment is shown! ++ * + * @param transferring Flag signaling if the file should be considered as downloading or uploading, + * although {@link FileDownloaderBinder#isDownloading(Account, OCFile)} and + * {@link FileUploaderBinder#isUploading(Account, OCFile)} return false. - * ++ * ++ * @param refresh If 'true', try to refresh the hold file from the database + */ - public void updateFileDetails(boolean transferring) { ++ public void updateFileDetails(boolean transferring, boolean refresh) { + - if (mFile != null && mAccount != null && mLayout == R.layout.file_details_fragment) { ++ if (readyToShow()) { ++ ++ if (refresh && mStorageManager != null) { ++ mFile = mStorageManager.getFileByPath(mFile.getRemotePath()); ++ } + + // set file details + setFilename(mFile.getFileName()); + setFiletype(mFile.getMimetype()); + setFilesize(mFile.getFileLength()); + if(ocVersionSupportsTimeCreated()){ + setTimeCreated(mFile.getCreationTimestamp()); + } + + setTimeModified(mFile.getModificationTimestamp()); + + CheckBox cb = (CheckBox)getView().findViewById(R.id.fdKeepInSync); + cb.setChecked(mFile.keepInSync()); + + // configure UI for depending upon local state of the file + //if (FileDownloader.isDownloading(mAccount, mFile.getRemotePath()) || FileUploader.isUploading(mAccount, mFile.getRemotePath())) { + FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder(); + FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder(); + if (transferring || (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile)) || (uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile))) { + setButtonsForTransferring(); + + } else if (mFile.isDown()) { - // Update preview - if (mFile.getMimetype().startsWith("image/")) { - BitmapLoader bl = new BitmapLoader(); - bl.execute(new String[]{mFile.getStoragePath()}); - } + + setButtonsForDown(); + + } else { + // TODO load default preview image; when the local file is removed, the preview remains there + setButtonsForRemote(); + } + } + getView().invalidate(); + } + + + /** ++ * Checks if the fragment is ready to show details of a OCFile ++ * ++ * @return 'True' when the fragment is ready to show details of a file ++ */ ++ private boolean readyToShow() { ++ return (mFile != null && mAccount != null && mLayout == R.layout.file_details_fragment); ++ } ++ ++ ++ /** + * Updates the filename in view + * @param filename to set + */ + private void setFilename(String filename) { + TextView tv = (TextView) getView().findViewById(R.id.fdFilename); + if (tv != null) + tv.setText(filename); + } + + /** + * Updates the MIME type in view + * @param mimetype to set + */ + private void setFiletype(String mimetype) { + TextView tv = (TextView) getView().findViewById(R.id.fdType); + if (tv != null) { + String printableMimetype = DisplayUtils.convertMIMEtoPrettyPrint(mimetype);; + tv.setText(printableMimetype); + } + ImageView iv = (ImageView) getView().findViewById(R.id.fdIcon); + if (iv != null) { + iv.setImageResource(DisplayUtils.getResourceId(mimetype)); + } + } + + /** + * Updates the file size in view + * @param filesize in bytes to set + */ + private void setFilesize(long filesize) { + TextView tv = (TextView) getView().findViewById(R.id.fdSize); + if (tv != null) + tv.setText(DisplayUtils.bytesToHumanReadable(filesize)); + } + + /** + * Updates the time that the file was created in view + * @param milliseconds Unix time to set + */ + private void setTimeCreated(long milliseconds){ + TextView tv = (TextView) getView().findViewById(R.id.fdCreated); + TextView tvLabel = (TextView) getView().findViewById(R.id.fdCreatedLabel); + if(tv != null){ + tv.setText(DisplayUtils.unixTimeToHumanReadable(milliseconds)); + tv.setVisibility(View.VISIBLE); + tvLabel.setVisibility(View.VISIBLE); + } + } + + /** + * Updates the time that the file was last modified + * @param milliseconds Unix time to set + */ + private void setTimeModified(long milliseconds){ + TextView tv = (TextView) getView().findViewById(R.id.fdModified); + if(tv != null){ + tv.setText(DisplayUtils.unixTimeToHumanReadable(milliseconds)); + } + } + + /** + * Enables or disables buttons for a file being downloaded + */ + private void setButtonsForTransferring() { + if (!isEmpty()) { + Button downloadButton = (Button) getView().findViewById(R.id.fdDownloadBtn); + downloadButton.setText(R.string.common_cancel); + //downloadButton.setEnabled(false); + + // let's protect the user from himself ;) + ((Button) getView().findViewById(R.id.fdOpenBtn)).setEnabled(false); + ((Button) getView().findViewById(R.id.fdRenameBtn)).setEnabled(false); + ((Button) getView().findViewById(R.id.fdRemoveBtn)).setEnabled(false); + getView().findViewById(R.id.fdKeepInSync).setEnabled(false); ++ ++ // show the progress bar for the transfer ++ ProgressBar progressBar = (ProgressBar)getView().findViewById(R.id.fdProgressBar); ++ progressBar.setVisibility(View.VISIBLE); ++ TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText); ++ progressText.setVisibility(View.VISIBLE); ++ FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder(); ++ FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder(); ++ if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile)) { ++ progressText.setText(R.string.downloader_download_in_progress_ticker); ++ } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile)) { ++ progressText.setText(R.string.uploader_upload_in_progress_ticker); ++ } + } + } - ++ + /** + * Enables or disables buttons for a file locally available + */ + private void setButtonsForDown() { + if (!isEmpty()) { + Button downloadButton = (Button) getView().findViewById(R.id.fdDownloadBtn); + downloadButton.setText(R.string.filedetails_sync_file); + + ((Button) getView().findViewById(R.id.fdOpenBtn)).setEnabled(true); + ((Button) getView().findViewById(R.id.fdRenameBtn)).setEnabled(true); + ((Button) getView().findViewById(R.id.fdRemoveBtn)).setEnabled(true); + getView().findViewById(R.id.fdKeepInSync).setEnabled(true); ++ ++ // hides the progress bar ++ ProgressBar progressBar = (ProgressBar)getView().findViewById(R.id.fdProgressBar); ++ progressBar.setVisibility(View.GONE); ++ TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText); ++ progressText.setVisibility(View.GONE); + } + } + + /** + * Enables or disables buttons for a file not locally available + */ + private void setButtonsForRemote() { + if (!isEmpty()) { + Button downloadButton = (Button) getView().findViewById(R.id.fdDownloadBtn); + downloadButton.setText(R.string.filedetails_download); + + ((Button) getView().findViewById(R.id.fdOpenBtn)).setEnabled(false); + ((Button) getView().findViewById(R.id.fdRenameBtn)).setEnabled(true); + ((Button) getView().findViewById(R.id.fdRemoveBtn)).setEnabled(true); + getView().findViewById(R.id.fdKeepInSync).setEnabled(true); ++ ++ // hides the progress bar ++ ProgressBar progressBar = (ProgressBar)getView().findViewById(R.id.fdProgressBar); ++ progressBar.setVisibility(View.GONE); ++ TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText); ++ progressText.setVisibility(View.GONE); + } + } + + + /** + * In ownCloud 3.X.X and 4.X.X there is a bug that SabreDAV does not return + * the time that the file was created. There is a chance that this will + * be fixed in future versions. Use this method to check if this version of + * ownCloud has this fix. + * @return True, if ownCloud the ownCloud version is supporting creation time + */ + private boolean ocVersionSupportsTimeCreated(){ + /*if(mAccount != null){ + AccountManager accManager = (AccountManager) getActivity().getSystemService(Context.ACCOUNT_SERVICE); + OwnCloudVersion ocVersion = new OwnCloudVersion(accManager + .getUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION)); + if(ocVersion.compareTo(new OwnCloudVersion(0x030000)) < 0) { + return true; + } + }*/ + return false; + } + - - /** - * Interface to implement by any Activity that includes some instance of FileDetailFragment - * - * @author David A. Velasco - */ - public interface ContainerActivity extends TransferServiceGetter { - - /** - * Callback method invoked when the detail fragment wants to notice its container - * activity about a relevant state the file shown by the fragment. - * - * Added to notify to FileDisplayActivity about the need of refresh the files list. - * - * Currently called when: - * - a download is started; - * - a rename is completed; - * - a deletion is completed; - * - the 'inSync' flag is changed; - */ - public void onFileStateChanged(); - - } - - - /** - * Once the file download has finished -> update view - * @author Bartek Przybylski - */ - private class DownloadFinishReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME); + - if (!isEmpty() && accountName.equals(mAccount.name)) { - boolean downloadWasFine = intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false); - String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH); - if (mFile.getRemotePath().equals(downloadedRemotePath)) { - if (downloadWasFine) { - mFile = mStorageManager.getFileByPath(downloadedRemotePath); - } - updateFileDetails(false); // it updates the buttons; must be called although !downloadWasFine - } - } - } - } - - + /** + * Once the file upload has finished -> update view + * + * Being notified about the finish of an upload is necessary for the next sequence: + * 1. Upload a big file. + * 2. Force a synchronization; if it finished before the upload, the file in transfer will be included in the local database and in the file list + * of its containing folder; the the server includes it in the PROPFIND requests although it's not fully upload. + * 3. Click the file in the list to see its details. + * 4. Wait for the upload finishes; at this moment, the details view must be refreshed to enable the action buttons. + */ + private class UploadFinishReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + String accountName = intent.getStringExtra(FileUploader.ACCOUNT_NAME); + + if (!isEmpty() && accountName.equals(mAccount.name)) { + boolean uploadWasFine = intent.getBooleanExtra(FileUploader.EXTRA_UPLOAD_RESULT, false); + String uploadRemotePath = intent.getStringExtra(FileUploader.EXTRA_REMOTE_PATH); + boolean renamedInUpload = mFile.getRemotePath().equals(intent.getStringExtra(FileUploader.EXTRA_OLD_REMOTE_PATH)); + if (mFile.getRemotePath().equals(uploadRemotePath) || + renamedInUpload) { + if (uploadWasFine) { + mFile = mStorageManager.getFileByPath(uploadRemotePath); + } + if (renamedInUpload) { + String newName = (new File(uploadRemotePath)).getName(); + Toast msg = Toast.makeText(getActivity().getApplicationContext(), String.format(getString(R.string.filedetails_renamed_in_upload_msg), newName), Toast.LENGTH_LONG); + msg.show(); + } + getSherlockActivity().removeStickyBroadcast(intent); // not the best place to do this; a small refactorization of BroadcastReceivers should be done - updateFileDetails(false); // it updates the buttons; must be called although !uploadWasFine; interrupted uploads still leave an incomplete file in the server ++ updateFileDetails(false, false); // it updates the buttons; must be called although !uploadWasFine; interrupted uploads still leave an incomplete file in the server + } + } + } + } + + + // this is a temporary class for sharing purposes, it need to be replaced in transfer service + @SuppressWarnings("unused") + private class ShareRunnable implements Runnable { + private String mPath; + + public ShareRunnable(String path) { + mPath = path; + } + + public void run() { + AccountManager am = AccountManager.get(getActivity()); + Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity()); + OwnCloudVersion ocv = new OwnCloudVersion(am.getUserData(account, AccountAuthenticator.KEY_OC_VERSION)); + String url = am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL) + AccountUtils.getWebdavPath(ocv); + - Log_OC.d("share", "sharing for version " + ocv.toString()); ++ Log.d("share", "sharing for version " + ocv.toString()); + + if (ocv.compareTo(new OwnCloudVersion(0x040000)) >= 0) { + String APPS_PATH = "/apps/files_sharing/"; + String SHARE_PATH = "ajax/share.php"; + + String SHARED_PATH = "/apps/files_sharing/get.php?token="; + + final String WEBDAV_SCRIPT = "webdav.php"; + final String WEBDAV_FILES_LOCATION = "/files/"; + + WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(account, getActivity().getApplicationContext()); + HttpConnectionManagerParams params = new HttpConnectionManagerParams(); + params.setMaxConnectionsPerHost(wc.getHostConfiguration(), 5); + + //wc.getParams().setParameter("http.protocol.single-cookie-header", true); + //wc.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY); + + PostMethod post = new PostMethod(am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL) + APPS_PATH + SHARE_PATH); + + post.addRequestHeader("Content-type","application/x-www-form-urlencoded; charset=UTF-8" ); + post.addRequestHeader("Referer", am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL)); + List formparams = new ArrayList(); - Log_OC.d("share", mPath+""); ++ Log.d("share", mPath+""); + formparams.add(new BasicNameValuePair("sources",mPath)); + formparams.add(new BasicNameValuePair("uid_shared_with", "public")); + formparams.add(new BasicNameValuePair("permissions", "0")); + post.setRequestEntity(new StringRequestEntity(URLEncodedUtils.format(formparams, HTTP.UTF_8))); + + int status; + try { + PropFindMethod find = new PropFindMethod(url+"/"); + find.addRequestHeader("Referer", am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL)); - Log_OC.d("sharer", ""+ url+"/"); ++ Log.d("sharer", ""+ url+"/"); + + for (org.apache.commons.httpclient.Header a : find.getRequestHeaders()) { - Log_OC.d("sharer-h", a.getName() + ":"+a.getValue()); ++ Log.d("sharer-h", a.getName() + ":"+a.getValue()); + } + + int status2 = wc.executeMethod(find); + - Log_OC.d("sharer", "propstatus "+status2); ++ Log.d("sharer", "propstatus "+status2); + + GetMethod get = new GetMethod(am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL) + "/"); + get.addRequestHeader("Referer", am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL)); + + status2 = wc.executeMethod(get); + - Log_OC.d("sharer", "getstatus "+status2); - Log_OC.d("sharer", "" + get.getResponseBodyAsString()); ++ Log.d("sharer", "getstatus "+status2); ++ Log.d("sharer", "" + get.getResponseBodyAsString()); + + for (org.apache.commons.httpclient.Header a : get.getResponseHeaders()) { - Log_OC.d("sharer", a.getName() + ":"+a.getValue()); ++ Log.d("sharer", a.getName() + ":"+a.getValue()); + } + + status = wc.executeMethod(post); + for (org.apache.commons.httpclient.Header a : post.getRequestHeaders()) { - Log_OC.d("sharer-h", a.getName() + ":"+a.getValue()); ++ Log.d("sharer-h", a.getName() + ":"+a.getValue()); + } + for (org.apache.commons.httpclient.Header a : post.getResponseHeaders()) { - Log_OC.d("sharer", a.getName() + ":"+a.getValue()); ++ Log.d("sharer", a.getName() + ":"+a.getValue()); + } + String resp = post.getResponseBodyAsString(); - Log_OC.d("share", ""+post.getURI().toString()); - Log_OC.d("share", "returned status " + status); - Log_OC.d("share", " " +resp); ++ Log.d("share", ""+post.getURI().toString()); ++ Log.d("share", "returned status " + status); ++ Log.d("share", " " +resp); + + if(status != HttpStatus.SC_OK ||resp == null || resp.equals("") || resp.startsWith("false")) { + return; + } + + JSONObject jsonObject = new JSONObject (resp); + String jsonStatus = jsonObject.getString("status"); + if(!jsonStatus.equals("success")) throw new Exception("Error while sharing file status != success"); + + String token = jsonObject.getString("data"); + String uri = am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL) + SHARED_PATH + token; - Log_OC.d("Actions:shareFile ok", "url: " + uri); ++ Log.d("Actions:shareFile ok", "url: " + uri); + + } catch (Exception e) { + e.printStackTrace(); + } + + } else if (ocv.compareTo(new OwnCloudVersion(0x030000)) >= 0) { + + } + } + } + + public void onDismiss(EditNameDialog dialog) { + if (dialog.getResult()) { + String newFilename = dialog.getNewFilename(); - Log_OC.d(TAG, "name edit dialog dismissed with new name " + newFilename); ++ Log.d(TAG, "name edit dialog dismissed with new name " + newFilename); + mLastRemoteOperation = new RenameFileOperation( mFile, + mAccount, + newFilename, + new FileDataStorageManager(mAccount, getActivity().getContentResolver())); + WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getSherlockActivity().getApplicationContext()); + mLastRemoteOperation.execute(wc, this, mHandler); + boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity; + getActivity().showDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT); + } + } + + - class BitmapLoader extends AsyncTask { - @SuppressLint({ "NewApi", "NewApi", "NewApi" }) // to avoid Lint errors since Android SDK r20 - @Override - protected Bitmap doInBackground(String... params) { - Bitmap result = null; - if (params.length != 1) return result; - String storagePath = params[0]; - try { - - BitmapFactory.Options options = new Options(); - options.inScaled = true; - options.inPurgeable = true; - options.inJustDecodeBounds = true; - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD_MR1) { - options.inPreferQualityOverSpeed = false; - } - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) { - options.inMutable = false; - } - - result = BitmapFactory.decodeFile(storagePath, options); - options.inJustDecodeBounds = false; - - int width = options.outWidth; - int height = options.outHeight; - int scale = 1; - if (width >= 2048 || height >= 2048) { - scale = (int) Math.ceil((Math.ceil(Math.max(height, width) / 2048.))); - options.inSampleSize = scale; - } - Display display = getActivity().getWindowManager().getDefaultDisplay(); - Point size = new Point(); - int screenwidth; - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR2) { - display.getSize(size); - screenwidth = size.x; - } else { - screenwidth = display.getWidth(); - } - - Log_OC.e("ASD", "W " + width + " SW " + screenwidth); - - if (width > screenwidth) { - scale = (int) Math.ceil((float)width / screenwidth); - options.inSampleSize = scale; - } - - result = BitmapFactory.decodeFile(storagePath, options); - - Log_OC.e("ASD", "W " + options.outWidth + " SW " + options.outHeight); - - } catch (OutOfMemoryError e) { - result = null; - Log_OC.e(TAG, "Out of memory occured for file with size " + storagePath); - - } catch (NoSuchFieldError e) { - result = null; - Log_OC.e(TAG, "Error from access to unexisting field despite protection " + storagePath); - - } catch (Throwable t) { - result = null; - Log_OC.e(TAG, "Unexpected error while creating image preview " + storagePath, t); - } - return result; - } - @Override - protected void onPostExecute(Bitmap result) { - if (result != null && mPreview != null) { - mPreview.setImageBitmap(result); - } - } - - } - + /** + * {@inheritDoc} + */ + @Override + public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { + if (operation.equals(mLastRemoteOperation)) { + if (operation instanceof RemoveFileOperation) { + onRemoveFileOperationFinish((RemoveFileOperation)operation, result); + + } else if (operation instanceof RenameFileOperation) { + onRenameFileOperationFinish((RenameFileOperation)operation, result); + + } else if (operation instanceof SynchronizeFileOperation) { + onSynchronizeFileOperationFinish((SynchronizeFileOperation)operation, result); + } + } + } + + + private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) { + boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity; + getActivity().dismissDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT); + + if (result.isSuccess()) { + Toast msg = Toast.makeText(getActivity().getApplicationContext(), R.string.remove_success_msg, Toast.LENGTH_LONG); + msg.show(); + if (inDisplayActivity) { + // double pane + FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction(); + transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null)); // empty FileDetailFragment + transaction.commit(); + mContainerActivity.onFileStateChanged(); + } else { + getActivity().finish(); + } + + } else { + Toast msg = Toast.makeText(getActivity(), R.string.remove_fail_msg, Toast.LENGTH_LONG); + msg.show(); + if (result.isSslRecoverableException()) { + // TODO show the SSL warning dialog + } + } + } + + private void onRenameFileOperationFinish(RenameFileOperation operation, RemoteOperationResult result) { + boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity; + getActivity().dismissDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT); + + if (result.isSuccess()) { + updateFileDetails(((RenameFileOperation)operation).getFile(), mAccount); + mContainerActivity.onFileStateChanged(); + + } else { + if (result.getCode().equals(ResultCode.INVALID_LOCAL_FILE_NAME)) { + Toast msg = Toast.makeText(getActivity(), R.string.rename_local_fail_msg, Toast.LENGTH_LONG); + msg.show(); + // TODO throw again the new rename dialog + } else { + Toast msg = Toast.makeText(getActivity(), R.string.rename_server_fail_msg, Toast.LENGTH_LONG); + msg.show(); + if (result.isSslRecoverableException()) { + // TODO show the SSL warning dialog + } + } + } + } + + private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation, RemoteOperationResult result) { + boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity; + getActivity().dismissDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT); + + if (!result.isSuccess()) { + if (result.getCode() == ResultCode.SYNC_CONFLICT) { + Intent i = new Intent(getActivity(), ConflictsResolveActivity.class); + i.putExtra(ConflictsResolveActivity.EXTRA_FILE, mFile); + i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, mAccount); + startActivity(i); + + } else { + Toast msg = Toast.makeText(getActivity(), R.string.sync_file_fail_msg, Toast.LENGTH_LONG); + msg.show(); + } + + if (mFile.isDown()) { + setButtonsForDown(); + + } else { + setButtonsForRemote(); + } + + } else { + if (operation.transferWasRequested()) { ++ setButtonsForTransferring(); + mContainerActivity.onFileStateChanged(); // this is not working; FileDownloader won't do NOTHING at all until this method finishes, so + // checking the service to see if the file is downloading results in FALSE + } else { + Toast msg = Toast.makeText(getActivity(), R.string.sync_file_nothing_to_do_msg, Toast.LENGTH_LONG); + msg.show(); + if (mFile.isDown()) { + setButtonsForDown(); + + } else { + setButtonsForRemote(); + } + } + } + } ++ ++ ++ public void listenForTransferProgress() { ++ if (mProgressListener != null) { ++ if (mContainerActivity.getFileDownloaderBinder() != null) { ++ mContainerActivity.getFileDownloaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, mFile); ++ } ++ if (mContainerActivity.getFileUploaderBinder() != null) { ++ mContainerActivity.getFileUploaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, mFile); ++ } ++ } ++ } ++ ++ ++ public void leaveTransferProgress() { ++ if (mProgressListener != null) { ++ if (mContainerActivity.getFileDownloaderBinder() != null) { ++ mContainerActivity.getFileDownloaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, mFile); ++ } ++ if (mContainerActivity.getFileUploaderBinder() != null) { ++ mContainerActivity.getFileUploaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, mFile); ++ } ++ } ++ } ++ ++ ++ ++ /** ++ * Helper class responsible for updating the progress bar shown for file uploading or downloading ++ * ++ * @author David A. Velasco ++ */ ++ private class ProgressListener implements OnDatatransferProgressListener { ++ int mLastPercent = 0; ++ WeakReference mProgressBar = null; ++ ++ ProgressListener(ProgressBar progressBar) { ++ mProgressBar = new WeakReference(progressBar); ++ } ++ ++ @Override ++ public void onTransferProgress(long progressRate) { ++ // old method, nothing here ++ }; ++ ++ @Override ++ public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filename) { ++ int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer)); ++ if (percent != mLastPercent) { ++ ProgressBar pb = mProgressBar.get(); ++ if (pb != null) { ++ pb.setProgress(percent); ++ pb.postInvalidate(); ++ } ++ } ++ mLastPercent = percent; ++ } ++ ++ }; + - } ++} diff --cc src/com/owncloud/android/ui/fragment/OCFileListFragment.java index ef5b7e11,90a06c0d..fe25025d --- a/src/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/src/com/owncloud/android/ui/fragment/OCFileListFragment.java @@@ -529,11 -522,7 +523,7 @@@ public class OCFileListFragment extend @Override public void onCancel(String callerTag) { - Log.d(TAG, "REMOVAL CANCELED"); + Log_OC.d(TAG, "REMOVAL CANCELED"); - if (mCurrentDialog != null) { - mCurrentDialog.dismiss(); - mCurrentDialog = null; - } } diff --cc src/eu/alefzero/webdav/ChunkFromFileChannelRequestEntity.java index 13c11042,8d3912d9..02554468 --- a/src/eu/alefzero/webdav/ChunkFromFileChannelRequestEntity.java +++ b/src/eu/alefzero/webdav/ChunkFromFileChannelRequestEntity.java @@@ -30,12 -30,12 +30,11 @@@ import java.util.Set import org.apache.commons.httpclient.methods.RequestEntity; +import com.owncloud.android.Log_OC; + import com.owncloud.android.network.ProgressiveDataTransferer; import eu.alefzero.webdav.OnDatatransferProgressListener; --import android.util.Log; -- /** * A RequestEntity that represents a PIECE of a file. diff --cc src/eu/alefzero/webdav/FileRequestEntity.java index b6ecae93,d60eabf1..2f8073c3 --- a/src/eu/alefzero/webdav/FileRequestEntity.java +++ b/src/eu/alefzero/webdav/FileRequestEntity.java @@@ -32,12 -32,12 +32,11 @@@ import java.util.Set import org.apache.commons.httpclient.methods.RequestEntity; +import com.owncloud.android.Log_OC; + import com.owncloud.android.network.ProgressiveDataTransferer; import eu.alefzero.webdav.OnDatatransferProgressListener; --import android.util.Log; -- /** * A RequestEntity that represents a File. diff --cc src/eu/alefzero/webdav/WebdavClient.java index 704e14af,df30dcb6..c4df9d16 --- a/src/eu/alefzero/webdav/WebdavClient.java +++ b/src/eu/alefzero/webdav/WebdavClient.java @@@ -1,343 -1,341 +1,342 @@@ -/* ownCloud Android client application - * Copyright (C) 2011 Bartek Przybylski - * Copyright (C) 2012-2013 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -package eu.alefzero.webdav; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; - -import org.apache.commons.httpclient.Credentials; -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.HttpConnectionManager; -import org.apache.commons.httpclient.HttpException; -import org.apache.commons.httpclient.HttpMethodBase; -import org.apache.commons.httpclient.HttpVersion; -import org.apache.commons.httpclient.UsernamePasswordCredentials; -import org.apache.commons.httpclient.auth.AuthScope; -import org.apache.commons.httpclient.methods.GetMethod; -import org.apache.commons.httpclient.methods.HeadMethod; -import org.apache.commons.httpclient.methods.PutMethod; -import org.apache.commons.httpclient.params.HttpMethodParams; -import org.apache.http.HttpStatus; -import org.apache.http.params.CoreProtocolPNames; -import org.apache.jackrabbit.webdav.client.methods.DavMethod; -import org.apache.jackrabbit.webdav.client.methods.DeleteMethod; -import org.apache.jackrabbit.webdav.client.methods.MkColMethod; - -import android.net.Uri; -import android.util.Log; - -public class WebdavClient extends HttpClient { - private Uri mUri; - private Credentials mCredentials; - final private static String TAG = "WebdavClient"; - private static final String USER_AGENT = "Android-ownCloud"; - - private OnDatatransferProgressListener mDataTransferListener; - static private byte[] sExhaustBuffer = new byte[1024]; - - /** - * Constructor - */ - public WebdavClient(HttpConnectionManager connectionMgr) { - super(connectionMgr); - Log.d(TAG, "Creating WebdavClient"); - getParams().setParameter(HttpMethodParams.USER_AGENT, USER_AGENT); - getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1); - } - - public void setCredentials(String username, String password) { - getParams().setAuthenticationPreemptive(true); - getState().setCredentials(AuthScope.ANY, - getCredentials(username, password)); - } - - private Credentials getCredentials(String username, String password) { - if (mCredentials == null) - mCredentials = new UsernamePasswordCredentials(username, password); - return mCredentials; - } - - /** - * Downloads a file in remoteFilepath to the local targetPath. - * - * @param remoteFilepath Path to the file in the remote server, URL DECODED. - * @param targetFile Local path to save the downloaded file. - * @return 'True' when the file is successfully downloaded. - */ - public boolean downloadFile(String remoteFilePath, File targetFile) { - boolean ret = false; - GetMethod get = new GetMethod(mUri.toString() + WebdavUtils.encodePath(remoteFilePath)); - - try { - int status = executeMethod(get); - if (status == HttpStatus.SC_OK) { - targetFile.createNewFile(); - BufferedInputStream bis = new BufferedInputStream( - get.getResponseBodyAsStream()); - FileOutputStream fos = new FileOutputStream(targetFile); - - byte[] bytes = new byte[4096]; - int readResult; - while ((readResult = bis.read(bytes)) != -1) { - if (mDataTransferListener != null) - mDataTransferListener.onTransferProgress(readResult); - fos.write(bytes, 0, readResult); - } - fos.close(); - ret = true; - } else { - exhaustResponse(get.getResponseBodyAsStream()); - } - Log.e(TAG, "Download of " + remoteFilePath + " to " + targetFile + " finished with HTTP status " + status + (!ret?"(FAIL)":"")); - } catch (Exception e) { - logException(e, "dowloading " + remoteFilePath); - - } finally { - if (!ret && targetFile.exists()) { - targetFile.delete(); - } - get.releaseConnection(); // let the connection available for other methods - } - return ret; - } - - /** - * Deletes a remote file via webdav - * @param remoteFilePath Remote file path of the file to delete, in URL DECODED format. - * @return - */ - public boolean deleteFile(String remoteFilePath) { - boolean ret = false; - DavMethod delete = new DeleteMethod(mUri.toString() + WebdavUtils.encodePath(remoteFilePath)); - try { - int status = executeMethod(delete); - ret = (status == HttpStatus.SC_OK || status == HttpStatus.SC_ACCEPTED || status == HttpStatus.SC_NO_CONTENT); - exhaustResponse(delete.getResponseBodyAsStream()); - - Log.e(TAG, "DELETE of " + remoteFilePath + " finished with HTTP status " + status + (!ret?"(FAIL)":"")); - - } catch (Exception e) { - logException(e, "deleting " + remoteFilePath); - - } finally { - delete.releaseConnection(); // let the connection available for other methods - } - return ret; - } - - - public void setDataTransferProgressListener(OnDatatransferProgressListener listener) { - mDataTransferListener = listener; - } - - /** - * Creates or update a file in the remote server with the contents of a local file. - * - * @param localFile Path to the local file to upload. - * @param remoteTarget Remote path to the file to create or update, URL DECODED - * @param contentType MIME type of the file. - * @return Status HTTP code returned by the server. - * @throws IOException When a transport error that could not be recovered occurred while uploading the file to the server. - * @throws HttpException When a violation of the HTTP protocol occurred. - */ - public int putFile(String localFile, String remoteTarget, String contentType) throws HttpException, IOException { - int status = -1; - PutMethod put = new PutMethod(mUri.toString() + WebdavUtils.encodePath(remoteTarget)); - - try { - File f = new File(localFile); - FileRequestEntity entity = new FileRequestEntity(f, contentType); - entity.addDatatransferProgressListener(mDataTransferListener); - put.setRequestEntity(entity); - status = executeMethod(put); - - exhaustResponse(put.getResponseBodyAsStream()); - - } finally { - put.releaseConnection(); // let the connection available for other methods - } - return status; - } - - /** - * Tries to log in to the current URI, with the current credentials - * - * @return A {@link HttpStatus}-Code of the result. SC_OK is good. - */ - public int tryToLogin() { - int status = 0; - HeadMethod head = new HeadMethod(mUri.toString()); - try { - status = executeMethod(head); - boolean result = status == HttpStatus.SC_OK; - Log.d(TAG, "HEAD for " + mUri + " finished with HTTP status " + status + (!result?"(FAIL)":"")); - exhaustResponse(head.getResponseBodyAsStream()); - - } catch (Exception e) { - logException(e, "trying to login at " + mUri.toString()); - - } finally { - head.releaseConnection(); - } - return status; - } - - /** - * Creates a remote directory with the received path. - * - * @param path Path of the directory to create, URL DECODED - * @return 'True' when the directory is successfully created - */ - public boolean createDirectory(String path) { - boolean result = false; - int status = -1; - MkColMethod mkcol = new MkColMethod(mUri.toString() + WebdavUtils.encodePath(path)); - try { - Log.d(TAG, "Creating directory " + path); - status = executeMethod(mkcol); - Log.d(TAG, "Status returned: " + status); - result = mkcol.succeeded(); - - Log.d(TAG, "MKCOL to " + path + " finished with HTTP status " + status + (!result?"(FAIL)":"")); - exhaustResponse(mkcol.getResponseBodyAsStream()); - - } catch (Exception e) { - logException(e, "creating directory " + path); - - } finally { - mkcol.releaseConnection(); // let the connection available for other methods - } - return result; - } - - - /** - * Check if a file exists in the OC server - * - * @return 'true' if the file exists; 'false' it doesn't exist - * @throws Exception When the existence could not be determined - */ - public boolean existsFile(String path) throws IOException, HttpException { - HeadMethod head = new HeadMethod(mUri.toString() + WebdavUtils.encodePath(path)); - try { - int status = executeMethod(head); - Log.d(TAG, "HEAD to " + path + " finished with HTTP status " + status + ((status != HttpStatus.SC_OK)?"(FAIL)":"")); - exhaustResponse(head.getResponseBodyAsStream()); - return (status == HttpStatus.SC_OK); - - } finally { - head.releaseConnection(); // let the connection available for other methods - } - } - - - /** - * Requests the received method with the received timeout (milliseconds). - * - * Executes the method through the inherited HttpClient.executedMethod(method). - * - * Sets the socket and connection timeouts only for the method received. - * - * The timeouts are both in milliseconds; 0 means 'infinite'; < 0 means 'do not change the default' - * - * @param method HTTP method request. - * @param readTimeout Timeout to set for data reception - * @param conntionTimout Timeout to set for connection establishment - */ - public int executeMethod(HttpMethodBase method, int readTimeout, int connectionTimeout) throws HttpException, IOException { - int oldSoTimeout = getParams().getSoTimeout(); - int oldConnectionTimeout = getHttpConnectionManager().getParams().getConnectionTimeout(); - try { - if (readTimeout >= 0) { - method.getParams().setSoTimeout(readTimeout); // this should be enough... - getParams().setSoTimeout(readTimeout); // ... but this looks like necessary for HTTPS - } - if (connectionTimeout >= 0) { - getHttpConnectionManager().getParams().setConnectionTimeout(connectionTimeout); - } - return executeMethod(method); - } finally { - getParams().setSoTimeout(oldSoTimeout); - getHttpConnectionManager().getParams().setConnectionTimeout(oldConnectionTimeout); - } - } - - /** - * Exhausts a not interesting HTTP response. Encouraged by HttpClient documentation. - * - * @param responseBodyAsStream InputStream with the HTTP response to exhaust. - */ - public void exhaustResponse(InputStream responseBodyAsStream) { - if (responseBodyAsStream != null) { - try { - while (responseBodyAsStream.read(sExhaustBuffer) >= 0); - responseBodyAsStream.close(); - - } catch (IOException io) { - Log.e(TAG, "Unexpected exception while exhausting not interesting HTTP response; will be IGNORED", io); - } - } - } - - - /** - * Logs an exception triggered in a HTTP request. - * - * @param e Caught exception. - * @param doing Suffix to add at the end of the logged message. - */ - private void logException(Exception e, String doing) { - if (e instanceof HttpException) { - Log.e(TAG, "HTTP violation while " + doing, e); - - } else if (e instanceof IOException) { - Log.e(TAG, "Unrecovered transport exception while " + doing, e); - - } else { - Log.e(TAG, "Unexpected exception while " + doing, e); - } - } - - - /** - * Sets the connection and wait-for-data timeouts to be applied by default to the methods performed by this client. - */ - public void setDefaultTimeouts(int defaultDataTimeout, int defaultConnectionTimeout) { - getParams().setSoTimeout(defaultDataTimeout); - getHttpConnectionManager().getParams().setConnectionTimeout(defaultConnectionTimeout); - } - - /** - * Sets the base URI for the helper methods that receive paths as parameters, instead of full URLs - * @param uri - */ - public void setBaseUri(Uri uri) { - mUri = uri; - } - - public Uri getBaseUri() { - return mUri; - } - -} +/* ownCloud Android client application + * Copyright (C) 2011 Bartek Przybylski + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package eu.alefzero.webdav; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.commons.httpclient.Credentials; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpConnectionManager; +import org.apache.commons.httpclient.HttpException; +import org.apache.commons.httpclient.HttpMethodBase; +import org.apache.commons.httpclient.HttpVersion; +import org.apache.commons.httpclient.UsernamePasswordCredentials; +import org.apache.commons.httpclient.auth.AuthScope; +import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.commons.httpclient.methods.HeadMethod; +import org.apache.commons.httpclient.methods.PutMethod; +import org.apache.commons.httpclient.params.HttpMethodParams; +import org.apache.http.HttpStatus; +import org.apache.http.params.CoreProtocolPNames; +import org.apache.jackrabbit.webdav.client.methods.DavMethod; +import org.apache.jackrabbit.webdav.client.methods.DeleteMethod; +import org.apache.jackrabbit.webdav.client.methods.MkColMethod; + +import com.owncloud.android.Log_OC; + +import android.net.Uri; +import android.util.Log; + +public class WebdavClient extends HttpClient { + private Uri mUri; + private Credentials mCredentials; + final private static String TAG = "WebdavClient"; + private static final String USER_AGENT = "Android-ownCloud"; + + private OnDatatransferProgressListener mDataTransferListener; + static private byte[] sExhaustBuffer = new byte[1024]; + + /** + * Constructor + */ + public WebdavClient(HttpConnectionManager connectionMgr) { + super(connectionMgr); + Log_OC.d(TAG, "Creating WebdavClient"); + getParams().setParameter(HttpMethodParams.USER_AGENT, USER_AGENT); + getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1); + } + + public void setCredentials(String username, String password) { + getParams().setAuthenticationPreemptive(true); + getState().setCredentials(AuthScope.ANY, + getCredentials(username, password)); + } + + private Credentials getCredentials(String username, String password) { + if (mCredentials == null) + mCredentials = new UsernamePasswordCredentials(username, password); + return mCredentials; + } + + /** + * Downloads a file in remoteFilepath to the local targetPath. + * + * @param remoteFilepath Path to the file in the remote server, URL DECODED. + * @param targetFile Local path to save the downloaded file. + * @return 'True' when the file is successfully downloaded. + */ + public boolean downloadFile(String remoteFilePath, File targetFile) { + boolean ret = false; + GetMethod get = new GetMethod(mUri.toString() + WebdavUtils.encodePath(remoteFilePath)); + + try { + int status = executeMethod(get); + if (status == HttpStatus.SC_OK) { + targetFile.createNewFile(); + BufferedInputStream bis = new BufferedInputStream( + get.getResponseBodyAsStream()); + FileOutputStream fos = new FileOutputStream(targetFile); + + byte[] bytes = new byte[4096]; + int readResult; + while ((readResult = bis.read(bytes)) != -1) { + if (mDataTransferListener != null) + mDataTransferListener.onTransferProgress(readResult); + fos.write(bytes, 0, readResult); + } + fos.close(); + ret = true; + } else { + exhaustResponse(get.getResponseBodyAsStream()); + } + Log_OC.e(TAG, "Download of " + remoteFilePath + " to " + targetFile + " finished with HTTP status " + status + (!ret?"(FAIL)":"")); ++ + } catch (Exception e) { + logException(e, "dowloading " + remoteFilePath); + + } finally { + if (!ret && targetFile.exists()) { + targetFile.delete(); + } + get.releaseConnection(); // let the connection available for other methods + } + return ret; + } + + /** + * Deletes a remote file via webdav + * @param remoteFilePath Remote file path of the file to delete, in URL DECODED format. + * @return + */ + public boolean deleteFile(String remoteFilePath) { + boolean ret = false; + DavMethod delete = new DeleteMethod(mUri.toString() + WebdavUtils.encodePath(remoteFilePath)); + try { + int status = executeMethod(delete); + ret = (status == HttpStatus.SC_OK || status == HttpStatus.SC_ACCEPTED || status == HttpStatus.SC_NO_CONTENT); + exhaustResponse(delete.getResponseBodyAsStream()); + - Log.e(TAG, "DELETE of " + remoteFilePath + " finished with HTTP status " + status + (!ret?"(FAIL)":"")); ++ Log_OC.e(TAG, "DELETE of " + remoteFilePath + " finished with HTTP status " + status + (!ret?"(FAIL)":"")); + + } catch (Exception e) { + logException(e, "deleting " + remoteFilePath); + + } finally { + delete.releaseConnection(); // let the connection available for other methods + } + return ret; + } + + + public void setDataTransferProgressListener(OnDatatransferProgressListener listener) { + mDataTransferListener = listener; + } + + /** + * Creates or update a file in the remote server with the contents of a local file. + * + * @param localFile Path to the local file to upload. + * @param remoteTarget Remote path to the file to create or update, URL DECODED + * @param contentType MIME type of the file. + * @return Status HTTP code returned by the server. + * @throws IOException When a transport error that could not be recovered occurred while uploading the file to the server. + * @throws HttpException When a violation of the HTTP protocol occurred. + */ + public int putFile(String localFile, String remoteTarget, String contentType) throws HttpException, IOException { + int status = -1; + PutMethod put = new PutMethod(mUri.toString() + WebdavUtils.encodePath(remoteTarget)); + + try { + File f = new File(localFile); + FileRequestEntity entity = new FileRequestEntity(f, contentType); - entity.addOnDatatransferProgressListener(mDataTransferListener); ++ entity.addDatatransferProgressListener(mDataTransferListener); + put.setRequestEntity(entity); + status = executeMethod(put); + + exhaustResponse(put.getResponseBodyAsStream()); + + } finally { + put.releaseConnection(); // let the connection available for other methods + } + return status; + } + + /** + * Tries to log in to the current URI, with the current credentials + * + * @return A {@link HttpStatus}-Code of the result. SC_OK is good. + */ + public int tryToLogin() { + int status = 0; + HeadMethod head = new HeadMethod(mUri.toString()); + try { + status = executeMethod(head); + boolean result = status == HttpStatus.SC_OK; + Log_OC.d(TAG, "HEAD for " + mUri + " finished with HTTP status " + status + (!result?"(FAIL)":"")); + exhaustResponse(head.getResponseBodyAsStream()); + + } catch (Exception e) { + logException(e, "trying to login at " + mUri.toString()); + + } finally { + head.releaseConnection(); + } + return status; + } + + /** + * Creates a remote directory with the received path. + * + * @param path Path of the directory to create, URL DECODED + * @return 'True' when the directory is successfully created + */ + public boolean createDirectory(String path) { + boolean result = false; + int status = -1; + MkColMethod mkcol = new MkColMethod(mUri.toString() + WebdavUtils.encodePath(path)); + try { + Log_OC.d(TAG, "Creating directory " + path); + status = executeMethod(mkcol); + Log_OC.d(TAG, "Status returned: " + status); + result = mkcol.succeeded(); + + Log_OC.d(TAG, "MKCOL to " + path + " finished with HTTP status " + status + (!result?"(FAIL)":"")); + exhaustResponse(mkcol.getResponseBodyAsStream()); + + } catch (Exception e) { + logException(e, "creating directory " + path); + + } finally { + mkcol.releaseConnection(); // let the connection available for other methods + } + return result; + } + + + /** + * Check if a file exists in the OC server + * + * @return 'true' if the file exists; 'false' it doesn't exist + * @throws Exception When the existence could not be determined + */ + public boolean existsFile(String path) throws IOException, HttpException { + HeadMethod head = new HeadMethod(mUri.toString() + WebdavUtils.encodePath(path)); + try { + int status = executeMethod(head); + Log_OC.d(TAG, "HEAD to " + path + " finished with HTTP status " + status + ((status != HttpStatus.SC_OK)?"(FAIL)":"")); + exhaustResponse(head.getResponseBodyAsStream()); + return (status == HttpStatus.SC_OK); + + } finally { + head.releaseConnection(); // let the connection available for other methods + } + } + - + /** + * Requests the received method with the received timeout (milliseconds). + * + * Executes the method through the inherited HttpClient.executedMethod(method). + * + * Sets the socket and connection timeouts only for the method received. + * + * The timeouts are both in milliseconds; 0 means 'infinite'; < 0 means 'do not change the default' + * + * @param method HTTP method request. + * @param readTimeout Timeout to set for data reception + * @param conntionTimout Timeout to set for connection establishment + */ + public int executeMethod(HttpMethodBase method, int readTimeout, int connectionTimeout) throws HttpException, IOException { + int oldSoTimeout = getParams().getSoTimeout(); + int oldConnectionTimeout = getHttpConnectionManager().getParams().getConnectionTimeout(); + try { + if (readTimeout >= 0) { + method.getParams().setSoTimeout(readTimeout); // this should be enough... + getParams().setSoTimeout(readTimeout); // ... but this looks like necessary for HTTPS + } + if (connectionTimeout >= 0) { + getHttpConnectionManager().getParams().setConnectionTimeout(connectionTimeout); + } + return executeMethod(method); + } finally { + getParams().setSoTimeout(oldSoTimeout); + getHttpConnectionManager().getParams().setConnectionTimeout(oldConnectionTimeout); + } + } + + /** + * Exhausts a not interesting HTTP response. Encouraged by HttpClient documentation. + * + * @param responseBodyAsStream InputStream with the HTTP response to exhaust. + */ + public void exhaustResponse(InputStream responseBodyAsStream) { + if (responseBodyAsStream != null) { + try { + while (responseBodyAsStream.read(sExhaustBuffer) >= 0); + responseBodyAsStream.close(); + + } catch (IOException io) { + Log_OC.e(TAG, "Unexpected exception while exhausting not interesting HTTP response; will be IGNORED", io); + } + } + } + - + /** + * Logs an exception triggered in a HTTP request. + * + * @param e Caught exception. + * @param doing Suffix to add at the end of the logged message. + */ + private void logException(Exception e, String doing) { + if (e instanceof HttpException) { + Log_OC.e(TAG, "HTTP violation while " + doing, e); + + } else if (e instanceof IOException) { + Log_OC.e(TAG, "Unrecovered transport exception while " + doing, e); + + } else { + Log_OC.e(TAG, "Unexpected exception while " + doing, e); + } + } + + + /** + * Sets the connection and wait-for-data timeouts to be applied by default to the methods performed by this client. + */ + public void setDefaultTimeouts(int defaultDataTimeout, int defaultConnectionTimeout) { + getParams().setSoTimeout(defaultDataTimeout); + getHttpConnectionManager().getParams().setConnectionTimeout(defaultConnectionTimeout); + } + + /** + * Sets the base URI for the helper methods that receive paths as parameters, instead of full URLs + * @param uri + */ + public void setBaseUri(Uri uri) { + mUri = uri; + } + + public Uri getBaseUri() { + return mUri; + } + +}