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=-c 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 --combined 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 --combined res/values/strings.xml index 0bc775c7,f54a5d56..415041ba --- a/res/values/strings.xml +++ b/res/values/strings.xml @@@ -2,7 -2,6 +2,6 @@@ ownCloud - What\'s new Password: Username: Login @@@ -15,8 -14,7 +14,8 @@@ Settings Setup Account There is no account set up on your device. In order to use this App, you need to create one. - %1$s Android App\n\nversion: %2$s + Android App + version: Refresh Upload Content from other apps @@@ -24,8 -22,9 +23,9 @@@ Create directory Search Settings + Details + General - Device tracking Add new session Create image thumbnails Select an account @@@ -41,12 -40,6 +41,12 @@@ Protect your client Enable instant uploads Instantly upload photos taken by camera + Enable Logging + This is used to log problems + Logging History + This shows the recorded logs + Delete History + URL Username Password @@@ -62,6 -55,7 +62,7 @@@ Connect to your %1$s Connect Upload + Choose upload directory: No account found There are no %1$s accounts on your device. Please setup an account first. Setup @@@ -91,6 -85,8 +92,8 @@@ Save & Exit Leave %1$s Error + Loading … + Unknown error About Delete account Create account @@@ -110,6 -106,7 +113,7 @@@ %1$s was successfully downloaded Download failed Download of %1$s could not be completed + Not downloaded yet Choose account Contacts Synchronization failed @@@ -139,7 -136,28 +143,28 @@@ Incorrect App PIN App PIN removed App PIN stored - + + "%1$s music player" + "%1$s (playing)" + "%1$s (loading)" + "%1$s playback finished" + No media file found + No account provided + File not in a valid account + Unsupported media codec + Media file could not be read + Media file not correctly encoded + Too much time trying to play + Media file cannot be streamed + Media file cannot be played with the stock media player + Security error trying to play %1$s + Input error trying to play %1$s + Unexpected error trying to play %1$s + Previous track button + Rewind button + Play or pause button + Fast forward button + Next track button 15 Minutes 30 Minutes @@@ -151,14 -169,14 +176,14 @@@ 60 - Trying to login… + Trying to login... No network connection No network connection has been detected, check your Internet connection and try again. Connect anyway Secure connection unavailable. The Application cannot establish a secure connection to the server. A non secure connection is available. You may continue or cancel. Connection established - Testing connection… + Testing connection... Malformed server configuration It seems that your server instance is not correctly configured. Contact your administrator for more details. Unknown error occurred! @@@ -225,10 -243,17 +250,17 @@@ Location: Validity: From: - To: - Signature: - Algorithm: - This is a placeholder + To: + Signature: + Algorithm: + + This is a placeholder + placeholder.txt + PNG Image + 389 KB + 2012/05/18 12:23 PM + 12:23:45 + Upload pictures via WiFi only /InstantUpload Update conflict @@@ -236,7 -261,11 +268,11 @@@ Keep both Overwrite Don\'t upload - + + Image preview + This image can not be shown + "Not enough memory to show this image + %1$s could not be copied to %2$s local directory Failed InstantUpload" @@@ -248,5 -277,6 +284,6 @@@ retry to upload the image: Load more Picrures do nothing you are not online for instant upload - - + Failure Message: + Please check your server configuration,maybe your quota is exceeded. + diff --combined src/com/owncloud/android/datamodel/FileDataStorageManager.java index 3d053e6e,41aad01f..f38302a9 --- a/src/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/src/com/owncloud/android/datamodel/FileDataStorageManager.java @@@ -26,7 -26,6 +26,7 @@@ import java.util.Iterator import java.util.List; import java.util.Vector; +import com.owncloud.android.Log_OC; import com.owncloud.android.db.ProviderMeta; import com.owncloud.android.db.ProviderMeta.ProviderTableMeta; import com.owncloud.android.utils.FileStorageUtils; @@@ -157,7 -156,7 +157,7 @@@ public class FileDataStorageManager imp cv, ProviderTableMeta._ID + "=?", new String[] { String.valueOf(file.getFileId()) }); } catch (RemoteException e) { - Log.e(TAG, + Log_OC.e(TAG, "Fail to insert insert file to database " + e.getMessage()); } @@@ -172,7 -171,7 +172,7 @@@ result_uri = getContentProvider().insert( ProviderTableMeta.CONTENT_URI_FILE, cv); } catch (RemoteException e) { - Log.e(TAG, + Log_OC.e(TAG, "Fail to insert insert file to database " + e.getMessage()); } @@@ -257,10 -256,10 +257,10 @@@ } } catch (OperationApplicationException e) { - Log.e(TAG, "Fail to update/insert list of files to database " + e.getMessage()); + Log_OC.e(TAG, "Fail to update/insert list of files to database " + e.getMessage()); } catch (RemoteException e) { - Log.e(TAG, "Fail to update/insert list of files to database " + e.getMessage()); + Log_OC.e(TAG, "Fail to update/insert list of files to database " + e.getMessage()); } // update new id in file objects for insertions @@@ -270,7 -269,7 +270,7 @@@ if (results[i].uri != null) { newId = Long.parseLong(results[i].uri.getPathSegments().get(1)); files.get(i).setFileId(newId); - //Log.v(TAG, "Found and added id in insertion for " + files.get(i).getRemotePath()); + //Log_OC.v(TAG, "Found and added id in insertion for " + files.get(i).getRemotePath()); } } } @@@ -322,7 -321,7 +322,7 @@@ ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?", new String[] { mAccount.name }, null); } catch (RemoteException e) { - Log.e(TAG, e.getMessage()); + Log_OC.e(TAG, e.getMessage()); return ret; } } else { @@@ -365,7 -364,7 +365,7 @@@ + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?", new String[] { value, mAccount.name }, null); } catch (RemoteException e) { - Log.e(TAG, + Log_OC.e(TAG, "Couldn't determine file existance, assuming non existance: " + e.getMessage()); return false; @@@ -395,7 -394,7 +395,7 @@@ + "=?", new String[] { value, mAccount.name }, null); } catch (RemoteException e) { - Log.e(TAG, "Could not get file details: " + e.getMessage()); + Log_OC.e(TAG, "Could not get file details: " + e.getMessage()); c = null; } } @@@ -518,7 -517,7 +518,7 @@@ ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + ProviderTableMeta.FILE_PATH + " LIKE ?", new String[] { mAccount.name, dir.getRemotePath() + "%" }, null); } catch (RemoteException e) { - Log.e(TAG, e.getMessage()); + Log_OC.e(TAG, e.getMessage()); } } else { c = getContentResolver().query(ProviderTableMeta.CONTENT_URI, @@@ -559,13 -558,30 +559,30 @@@ } } catch (OperationApplicationException e) { - Log.e(TAG, "Fail to update descendants of " + dir.getFileId() + " in database", e); + Log_OC.e(TAG, "Fail to update descendants of " + dir.getFileId() + " in database", e); } catch (RemoteException e) { - Log.e(TAG, "Fail to update desendants of " + dir.getFileId() + " in database", e); + Log_OC.e(TAG, "Fail to update desendants of " + dir.getFileId() + " in database", e); } } } + @Override + public Vector getDirectoryImages(OCFile directory) { + Vector ret = new Vector(); + if (directory != null) { + // TODO better implementation, filtering in the access to database (if possible) instead of here + Vector tmp = getDirectoryContent(directory); + OCFile current = null; + for (int i=0; i { @@@ -264,7 -265,7 +264,7 @@@ * Does nothing if the new name is null, empty or includes "/" ; or if the file is the root directory */ public void setFileName(String name) { - Log.d(TAG, "OCFile name changin from " + mRemotePath); + Log_OC.d(TAG, "OCFile name changin from " + mRemotePath); if (name != null && name.length() > 0 && !name.contains(PATH_SEPARATOR) && !mRemotePath.equals(PATH_SEPARATOR)) { String parent = (new File(getRemotePath())).getParent(); parent = (parent.endsWith(PATH_SEPARATOR)) ? parent : parent + PATH_SEPARATOR; @@@ -272,7 -273,7 +272,7 @@@ if (isDirectory()) { mRemotePath += PATH_SEPARATOR; } - Log.d(TAG, "OCFile name changed to " + mRemotePath); + Log_OC.d(TAG, "OCFile name changed to " + mRemotePath); } } @@@ -457,4 -458,30 +457,30 @@@ return 0; } + /** @return 'True' if the file contains audio */ + public boolean isAudio() { + return (mMimeType != null && mMimeType.startsWith("audio/")); + } + + /** @return 'True' if the file contains video */ + public boolean isVideo() { + return (mMimeType != null && mMimeType.startsWith("video/")); + } + + /** @return 'True' if the file contains an image */ + public boolean isImage() { + return ((mMimeType != null && mMimeType.startsWith("image/")) || + getMimeTypeFromName().startsWith("image/")); + } + + public String getMimeTypeFromName() { + String extension = ""; + int pos = mRemotePath.lastIndexOf('.'); + if (pos >= 0) { + extension = mRemotePath.substring(pos + 1); + } + String result = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase()); + return (result != null) ? result : ""; + } + } diff --combined 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 @@@ -17,8 -17,6 +17,8 @@@ */ package com.owncloud.android.db; +import com.owncloud.android.Log_OC; + import android.content.ContentValues; import android.content.Context; import android.database.Cursor; @@@ -52,21 -50,23 +52,23 @@@ public class DbHandler mDB.close(); } - public boolean putFileForLater(String filepath, String account) { + public boolean putFileForLater(String filepath, String account, String message) { ContentValues cv = new ContentValues(); 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; } @@@ -89,7 -89,7 +91,7 @@@ */ public boolean removeIUPendingFile(String localPath) { long result = mDB.delete(TABLE_INSTANT_UPLOAD, "path = ?", new String[] { localPath }); - Log.d(TABLE_INSTANT_UPLOAD, "delete returns with: " + result + " for file: " + localPath); + Log_OC.d(TABLE_INSTANT_UPLOAD, "delete returns with: " + result + " for file: " + localPath); return result != 0; } @@@ -102,12 -102,15 +104,15 @@@ @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + TABLE_INSTANT_UPLOAD + " (" + " _id INTEGER PRIMARY KEY, " + " path TEXT," - + " account TEXT,attempt INTEGER);"); + + " account TEXT,attempt INTEGER,message TEXT);"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - db.execSQL("ALTER TABLE " + TABLE_INSTANT_UPLOAD + " ADD COLUMN attempt;"); + if (oldVersion < 2) { + db.execSQL("ALTER TABLE " + TABLE_INSTANT_UPLOAD + " ADD COLUMN attempt INTEGER;"); + } + db.execSQL("ALTER TABLE " + TABLE_INSTANT_UPLOAD + " ADD COLUMN message TEXT;"); } } diff --combined src/com/owncloud/android/files/InstantUploadBroadcastReceiver.java index f6e531dd,1232fa13..c946ec92 --- a/src/com/owncloud/android/files/InstantUploadBroadcastReceiver.java +++ b/src/com/owncloud/android/files/InstantUploadBroadcastReceiver.java @@@ -35,21 -35,20 +35,21 @@@ import android.util.Log import android.webkit.MimeTypeMap; import com.owncloud.android.AccountUtils; +import com.owncloud.android.Log_OC; import com.owncloud.android.authenticator.AccountAuthenticator; import com.owncloud.android.db.DbHandler; import com.owncloud.android.files.services.FileUploader; + import com.owncloud.android.utils.FileStorageUtils; public class InstantUploadBroadcastReceiver extends BroadcastReceiver { - public static String INSTANT_UPLOAD_DIR = "/InstantUpload/"; private static String TAG = "PhotoTakenBroadcastReceiver"; private static final String[] CONTENT_PROJECTION = { Media.DATA, Media.DISPLAY_NAME, Media.MIME_TYPE, Media.SIZE }; private static String NEW_PHOTO_ACTION = "com.android.camera.NEW_PICTURE"; @Override public void onReceive(Context context, Intent intent) { - Log.d(TAG, "Received: " + intent.getAction()); + Log_OC.d(TAG, "Received: " + intent.getAction()); if (intent.getAction().equals(android.net.ConnectivityManager.CONNECTIVITY_ACTION)) { handleConnectivityAction(context, intent); } else if (intent.getAction().equals(NEW_PHOTO_ACTION)) { @@@ -57,7 -56,7 +57,7 @@@ } else if (intent.getAction().equals(FileUploader.UPLOAD_FINISH_MESSAGE)) { handleUploadFinished(context, intent); } else { - Log.e(TAG, "Incorrect intent sent: " + intent.getAction()); + Log_OC.e(TAG, "Incorrect intent sent: " + intent.getAction()); } } @@@ -67,7 -66,7 +67,7 @@@ DbHandler db = new DbHandler(context); String localPath = intent.getStringExtra(FileUploader.EXTRA_OLD_FILE_PATH); if (!db.removeIUPendingFile(localPath)) { - Log.w(TAG, "Tried to remove non existing instant upload file " + localPath); + Log_OC.w(TAG, "Tried to remove non existing instant upload file " + localPath); } db.close(); } @@@ -75,20 -74,20 +75,20 @@@ private void handleNewPhotoAction(Context context, Intent intent) { if (!instantUploadEnabled(context)) { - Log.d(TAG, "Instant upload disabled, abording uploading"); + Log_OC.d(TAG, "Instant upload disabled, abording uploading"); return; } Account account = AccountUtils.getCurrentOwnCloudAccount(context); if (account == null) { - Log.w(TAG, "No owncloud account found for instant upload, aborting"); + Log_OC.w(TAG, "No owncloud account found for instant upload, aborting"); return; } Cursor c = context.getContentResolver().query(intent.getData(), CONTENT_PROJECTION, null, null, null); if (!c.moveToFirst()) { - Log.e(TAG, "Couldn't resolve given uri: " + intent.getDataString()); + Log_OC.e(TAG, "Couldn't resolve given uri: " + intent.getDataString()); return; } @@@ -97,11 -96,11 +97,11 @@@ String mime_type = c.getString(c.getColumnIndex(Media.MIME_TYPE)); c.close(); - Log.e(TAG, file_path + ""); + Log_OC.e(TAG, file_path + ""); // same always temporally the picture to upload DbHandler db = new DbHandler(context); - db.putFileForLater(file_path, account.name); + db.putFileForLater(file_path, account.name, null); db.close(); if (!isOnline(context) || (instantUploadViaWiFiOnly(context) && !isConnectedViaWiFi(context))) { @@@ -122,7 -121,7 +122,7 @@@ Intent i = new Intent(context, FileUploader.class); i.putExtra(FileUploader.KEY_ACCOUNT, account); i.putExtra(FileUploader.KEY_LOCAL_FILE, file_path); - i.putExtra(FileUploader.KEY_REMOTE_FILE, INSTANT_UPLOAD_DIR + file_name); + i.putExtra(FileUploader.KEY_REMOTE_FILE, FileStorageUtils.getInstantUploadFilePath(file_name)); i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE); i.putExtra(FileUploader.KEY_MIME_TYPE, mime_type); i.putExtra(FileUploader.KEY_INSTANT_UPLOAD, true); @@@ -132,7 -131,7 +132,7 @@@ private void handleConnectivityAction(Context context, Intent intent) { if (!instantUploadEnabled(context)) { - Log.d(TAG, "Instant upload disabled, abording uploading"); + Log_OC.d(TAG, "Instant upload disabled, abording uploading"); return; } @@@ -157,7 -156,7 +157,7 @@@ f.getName().substring(f.getName().lastIndexOf('.') + 1)); } catch (Throwable e) { - Log.e(TAG, "Trying to find out MIME type of a file without extension: " + f.getName()); + Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + f.getName()); } if (mimeType == null) mimeType = "application/octet-stream"; @@@ -165,13 -164,13 +165,13 @@@ Intent i = new Intent(context, FileUploader.class); i.putExtra(FileUploader.KEY_ACCOUNT, account); i.putExtra(FileUploader.KEY_LOCAL_FILE, file_path); - i.putExtra(FileUploader.KEY_REMOTE_FILE, INSTANT_UPLOAD_DIR + f.getName()); + i.putExtra(FileUploader.KEY_REMOTE_FILE, FileStorageUtils.getInstantUploadFilePath(f.getName())); i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE); i.putExtra(FileUploader.KEY_INSTANT_UPLOAD, true); context.startService(i); } else { - Log.w(TAG, "Instant upload file " + f.getAbsolutePath() + " dont exist anymore"); + Log_OC.w(TAG, "Instant upload file " + f.getAbsolutePath() + " dont exist anymore"); } } while (c.moveToNext()); } diff --combined 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 --combined src/com/owncloud/android/files/services/FileUploader.java index 57ce11ec,9d2bbcf2..efdae3d8 --- a/src/com/owncloud/android/files/services/FileUploader.java +++ b/src/com/owncloud/android/files/services/FileUploader.java @@@ -21,7 -21,9 +21,9 @@@ package com.owncloud.android.files.serv 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; @@@ -49,21 -51,22 +51,23 @@@ import android.webkit.MimeTypeMap import android.widget.RemoteViews; import android.widget.Toast; +import com.owncloud.android.Log_OC; import com.owncloud.android.R; import com.owncloud.android.authenticator.AccountAuthenticator; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.db.DbHandler; - import com.owncloud.android.files.InstantUploadBroadcastReceiver; import com.owncloud.android.network.OwnCloudClientUtils; import com.owncloud.android.operations.ChunkedUploadFileOperation; import com.owncloud.android.operations.RemoteOperationResult; import com.owncloud.android.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.operations.UploadFileOperation; + import com.owncloud.android.ui.activity.FailedUploadActivity; import com.owncloud.android.ui.activity.FileDetailActivity; import com.owncloud.android.ui.activity.InstantUploadActivity; import com.owncloud.android.ui.fragment.FileDetailFragment; + import com.owncloud.android.ui.preview.PreviewImageActivity; + import com.owncloud.android.ui.preview.PreviewImageFragment; import com.owncloud.android.utils.OwnCloudVersion; import eu.alefzero.webdav.OnDatatransferProgressListener; @@@ -119,8 -122,8 +123,8 @@@ public class FileUploader extends Servi /** * Builds a key for mPendingUploads from the account and file to upload * - * @param account Account where the file to download is stored - * @param file File to download + * @param account Account where the file to upload is stored + * @param file File to upload */ private String buildRemoteName(Account account, OCFile file) { return account.name + file.getRemotePath(); @@@ -148,7 -151,7 +152,7 @@@ @Override public void onCreate() { super.onCreate(); - Log.i(TAG, "mPendingUploads size:" + mPendingUploads.size()); + Log_OC.i(TAG, "mPendingUploads size:" + mPendingUploads.size()); mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); HandlerThread thread = new HandlerThread("FileUploaderThread", Process.THREAD_PRIORITY_BACKGROUND); thread.start(); @@@ -168,12 -171,12 +172,12 @@@ public int onStartCommand(Intent intent, int flags, int startId) { if (!intent.hasExtra(KEY_ACCOUNT) || !intent.hasExtra(KEY_UPLOAD_TYPE) || !(intent.hasExtra(KEY_LOCAL_FILE) || intent.hasExtra(KEY_FILE))) { - Log.e(TAG, "Not enough information provided in intent"); + Log_OC.e(TAG, "Not enough information provided in intent"); return Service.START_NOT_STICKY; } int uploadType = intent.getIntExtra(KEY_UPLOAD_TYPE, -1); if (uploadType == -1) { - Log.e(TAG, "Incorrect upload type provided"); + Log_OC.e(TAG, "Incorrect upload type provided"); return Service.START_NOT_STICKY; } Account account = intent.getParcelableExtra(KEY_ACCOUNT); @@@ -224,20 -227,20 +228,20 @@@ } if (intent.hasExtra(KEY_FILE) && files == null) { - Log.e(TAG, "Incorrect array for OCFiles provided in upload intent"); + Log_OC.e(TAG, "Incorrect array for OCFiles provided in upload intent"); return Service.START_NOT_STICKY; } else if (!intent.hasExtra(KEY_FILE)) { if (localPaths == null) { - Log.e(TAG, "Incorrect array for local paths provided in upload intent"); + Log_OC.e(TAG, "Incorrect array for local paths provided in upload intent"); return Service.START_NOT_STICKY; } if (remotePaths == null) { - Log.e(TAG, "Incorrect array for remote paths provided in upload intent"); + Log_OC.e(TAG, "Incorrect array for remote paths provided in upload intent"); return Service.START_NOT_STICKY; } if (localPaths.length != remotePaths.length) { - Log.e(TAG, "Different number of remote paths and local paths!"); + Log_OC.e(TAG, "Different number of remote paths and local paths!"); return Service.START_NOT_STICKY; } @@@ -272,19 -275,20 +276,20 @@@ } mPendingUploads.putIfAbsent(uploadKey, newUpload); newUpload.addDatatransferProgressListener(this); + newUpload.addDatatransferProgressListener((FileUploaderBinder)mBinder); requestedUploads.add(uploadKey); } } catch (IllegalArgumentException e) { - Log.e(TAG, "Not enough information provided in intent: " + e.getMessage()); + Log_OC.e(TAG, "Not enough information provided in intent: " + e.getMessage()); return START_NOT_STICKY; } catch (IllegalStateException e) { - Log.e(TAG, "Bad information provided in intent: " + e.getMessage()); + Log_OC.e(TAG, "Bad information provided in intent: " + e.getMessage()); return START_NOT_STICKY; } catch (Exception e) { - Log.e(TAG, "Unexpected exception while processing upload intent", e); + Log_OC.e(TAG, "Unexpected exception while processing upload intent", e); return START_NOT_STICKY; } @@@ -295,7 -299,7 +300,7 @@@ msg.obj = requestedUploads; mServiceHandler.sendMessage(msg); } - Log.i(TAG, "mPendingUploads size:" + mPendingUploads.size()); + Log_OC.i(TAG, "mPendingUploads size:" + mPendingUploads.size()); return Service.START_NOT_STICKY; } @@@ -310,6 -314,16 +315,16 @@@ public IBinder onBind(Intent arg0) { return mBinder; } + + /** + * Called when ALL the bound clients were onbound. + */ + @Override + public boolean onUnbind(Intent intent) { + ((FileUploaderBinder)mBinder).clearListeners(); + return false; // not accepting rebinding (default behaviour) + } + /** * Binder to let client components to perform operations on the queue of @@@ -317,8 -331,13 +332,13 @@@ * * It provides by itself the available operations. */ - public class FileUploaderBinder extends Binder { - + public class FileUploaderBinder extends Binder implements OnDatatransferProgressListener { + + /** + * Map of listeners that will be reported about progress of uploads from a {@link FileUploaderBinder} instance + */ + private Map mBoundListeners = new HashMap(); + /** * Cancels a pending or current upload of a remote file. * @@@ -334,13 -353,21 +354,21 @@@ upload.cancel(); } } + + + + public void clearListeners() { + mBoundListeners.clear(); + } + + + /** * Returns True when the file described by 'file' is being uploaded to * the ownCloud account 'account' or waiting for it * - * If 'file' is a directory, returns 'true' if some of its descendant - * files is downloading or waiting to download. + * If 'file' is a directory, returns 'true' if some of its descendant files is uploading or waiting to upload. * * @param account Owncloud account where the remote file will be stored. * @param file A file that could be in the queue of pending uploads @@@ -351,7 -378,7 +379,7 @@@ String targetKey = buildRemoteName(account, file); synchronized (mPendingUploads) { if (file.isDirectory()) { - // this can be slow if there are many downloads :( + // this can be slow if there are many uploads :( Iterator it = mPendingUploads.keySet().iterator(); boolean found = false; while (it.hasNext() && !found) { @@@ -363,6 -390,55 +391,55 @@@ } } } + + + /** + * Adds a listener interested in the progress of the upload 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 upload 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(mCurrentUpload.getAccount(), mCurrentUpload.getFile()); + OnDatatransferProgressListener boundListener = mBoundListeners.get(key); + if (boundListener != null) { + boundListener.onTransferProgress(progressRate, totalTransferredSoFar, totalToTransfer, fileName); + } + } + } /** @@@ -423,28 -499,10 +500,10 @@@ // / create remote folder for instant uploads if (mCurrentUpload.isRemoteFolderToBeCreated()) { - mUploadClient.createDirectory(InstantUploadBroadcastReceiver.INSTANT_UPLOAD_DIR); // ignoring - // result; - // fail - // could - // just - // mean - // that - // it - // already - // exists, - // but - // local - // database - // is - // not - // synchronized; - // the - // upload - // will - // be - // tried - // anyway + mUploadClient.createDirectory(InstantUploadService.INSTANT_UPLOAD_DIR); + // ignoring result fail could just mean that it already exists, + // but local database is not synchronized the upload will be + // tried anyway } // / perform the upload @@@ -458,7 -516,7 +517,7 @@@ } finally { synchronized (mPendingUploads) { mPendingUploads.remove(uploadKey); - Log.i(TAG, "Remove CurrentUploadItem from pending upload Item Map."); + Log_OC.i(TAG, "Remove CurrentUploadItem from pending upload Item Map."); } } @@@ -504,12 -562,12 +563,12 @@@ } result = new RemoteOperationResult(isMultiStatus, status); - Log.i(TAG, "Update: synchronizing properties for uploaded " + mCurrentUpload.getRemotePath() + ": " + Log_OC.i(TAG, "Update: synchronizing properties for uploaded " + mCurrentUpload.getRemotePath() + ": " + result.getLogMessage()); } catch (Exception e) { result = new RemoteOperationResult(e); - Log.e(TAG, "Update: synchronizing properties for uploaded " + mCurrentUpload.getRemotePath() + ": " + Log_OC.e(TAG, "Update: synchronizing properties for uploaded " + mCurrentUpload.getRemotePath() + ": " + result.getLogMessage(), e); } finally { @@@ -539,16 -597,15 +598,15 @@@ file.setMimetype(we.contentType()); file.setModificationTimestamp(we.modifiedTimestamp()); file.setModificationTimestampAtLastSyncForData(we.modifiedTimestamp()); - // file.setEtag(mCurrentDownload.getEtag()); // TODO Etag, where - // available + // file.setEtag(mCurrentUpload.getEtag()); // TODO Etag, where available } private boolean checkAndFixInstantUploadDirectory(FileDataStorageManager storageManager) { - OCFile instantUploadDir = storageManager.getFileByPath(InstantUploadBroadcastReceiver.INSTANT_UPLOAD_DIR); + OCFile instantUploadDir = storageManager.getFileByPath(InstantUploadService.INSTANT_UPLOAD_DIR); if (instantUploadDir == null) { // first instant upload in the account, or never account not // synchronized after the remote InstantUpload folder was created - OCFile newDir = new OCFile(InstantUploadBroadcastReceiver.INSTANT_UPLOAD_DIR); + OCFile newDir = new OCFile(InstantUploadService.INSTANT_UPLOAD_DIR); newDir.setMimetype("DIR"); OCFile path = storageManager.getFileByPath(OCFile.PATH_SEPARATOR); @@@ -585,7 -642,7 +643,7 @@@ mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension( remotePath.substring(remotePath.lastIndexOf('.') + 1)); } catch (IndexOutOfBoundsException e) { - Log.e(TAG, "Trying to find out MIME type of a file without extension: " + remotePath); + Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + remotePath); } } if (mimeType == null) { @@@ -616,6 -673,7 +674,7 @@@ * * @param upload Upload operation starting. */ + @SuppressWarnings("deprecation") private void notifyUploadStart(UploadFileOperation upload) { // / create status notification with a progress bar mLastPercent = 0; @@@ -629,10 -687,15 +688,15 @@@ mNotification.contentView.setTextViewText(R.id.status_text, String.format(getString(R.string.uploader_upload_in_progress_content), 0, upload.getFileName())); 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); + + /// includes a pending intent in the notification showing the details view of the file + Intent showDetailsIntent = null; + if (PreviewImageFragment.canBePreviewed(upload.getFile())) { + showDetailsIntent = new Intent(this, PreviewImageActivity.class); + } else { + showDetailsIntent = new Intent(this, FileDetailActivity.class); + showDetailsIntent.putExtra(FileDetailActivity.EXTRA_MODE, FileDetailActivity.MODE_DETAILS); + } showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, upload.getFile()); showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, upload.getAccount()); showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); @@@ -673,7 -736,7 +737,7 @@@ * @param upload Finished upload operation */ private void notifyUploadResult(RemoteOperationResult uploadResult, UploadFileOperation upload) { - Log.d(TAG, "NotifyUploadResult with resultCode: " + uploadResult.getCode()); + Log_OC.d(TAG, "NotifyUploadResult with resultCode: " + uploadResult.getCode()); if (uploadResult.isCancelled()) { // / cancelled operation -> silent removal of progress notification mNotificationManager.cancel(R.string.uploader_upload_in_progress_ticker); @@@ -687,10 -750,15 +751,15 @@@ // flag mNotification.flags |= Notification.FLAG_AUTO_CANCEL; mNotification.contentView = mDefaultNotificationContentView; - - // / includes a pending intent in the notification showing the - // details view of the file - Intent showDetailsIntent = new Intent(this, FileDetailActivity.class); + + /// includes a pending intent in the notification showing the details view of the file + Intent showDetailsIntent = null; + if (PreviewImageFragment.canBePreviewed(upload.getFile())) { + showDetailsIntent = new Intent(this, PreviewImageActivity.class); + } else { + showDetailsIntent = new Intent(this, FileDetailActivity.class); + showDetailsIntent.putExtra(FileDetailActivity.EXTRA_MODE, FileDetailActivity.MODE_DETAILS); + } showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, upload.getFile()); showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, upload.getAccount()); showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); @@@ -716,12 -784,6 +785,6 @@@ getString(R.string.uploader_upload_failed_ticker), System.currentTimeMillis()); finalNotification.flags |= Notification.FLAG_AUTO_CANCEL; - Intent detailUploudIntent = new Intent(this, InstantUploadActivity.class); - detailUploudIntent.putExtra(FileUploader.KEY_ACCOUNT, upload.getAccount()); - finalNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), - (int) System.currentTimeMillis(), detailUploudIntent, PendingIntent.FLAG_UPDATE_CURRENT - | PendingIntent.FLAG_ONE_SHOT); - String content = null; if (uploadResult.getCode() == ResultCode.LOCAL_STORAGE_FULL || uploadResult.getCode() == ResultCode.LOCAL_STORAGE_NOT_COPIED) { @@@ -729,21 -791,50 +792,50 @@@ // from a RemoteOperationResult and a RemoteOperation content = String.format(getString(R.string.error__upload__local_file_not_copied), upload.getFileName(), getString(R.string.app_name)); + } else if (uploadResult.getCode() == ResultCode.QUOTA_EXCEEDED) { + content = getString(R.string.failed_upload_quota_exceeded_text); } else { content = String .format(getString(R.string.uploader_upload_failed_content_single), upload.getFileName()); } - finalNotification.setLatestEventInfo(getApplicationContext(), - getString(R.string.uploader_upload_failed_ticker), content, finalNotification.contentIntent); - mNotificationManager.notify(R.string.uploader_upload_failed_ticker, finalNotification); + // we add only for instant-uploads the InstantUploadActivity and the + // db entry + Intent detailUploadIntent = null; + if (upload.isInstant() && InstantUploadActivity.IS_ENABLED) { + detailUploadIntent = new Intent(this, InstantUploadActivity.class); + detailUploadIntent.putExtra(FileUploader.KEY_ACCOUNT, upload.getAccount()); + } else { + detailUploadIntent = new Intent(this, FailedUploadActivity.class); + detailUploadIntent.putExtra(FailedUploadActivity.MESSAGE, content); + } + finalNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), + (int) System.currentTimeMillis(), detailUploadIntent, PendingIntent.FLAG_UPDATE_CURRENT + | PendingIntent.FLAG_ONE_SHOT); - DbHandler db = new DbHandler(this.getBaseContext()); - if (db.updateFileState(upload.getOriginalStoragePath(), DbHandler.UPLOAD_STATUS_UPLOAD_FAILED) == 0) { - db.putFileForLater(upload.getOriginalStoragePath(), upload.getAccount().name); + if (upload.isInstant()) { + DbHandler db = null; + try { + db = new DbHandler(this.getBaseContext()); + String message = uploadResult.getLogMessage() + " errorCode: " + uploadResult.getCode(); + Log.e(TAG, message + " Http-Code: " + uploadResult.getHttpCode()); + if (uploadResult.getCode() == ResultCode.QUOTA_EXCEEDED) { + message = getString(R.string.failed_upload_quota_exceeded_text); + } + if (db.updateFileState(upload.getOriginalStoragePath(), DbHandler.UPLOAD_STATUS_UPLOAD_FAILED, + message) == 0) { + db.putFileForLater(upload.getOriginalStoragePath(), upload.getAccount().name, message); + } + } finally { + if (db != null) { + db.close(); + } + } } - db.close(); + finalNotification.setLatestEventInfo(getApplicationContext(), + getString(R.string.uploader_upload_failed_ticker), content, finalNotification.contentIntent); + mNotificationManager.notify(R.string.uploader_upload_failed_ticker, finalNotification); } } diff --combined 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 { @@@ -44,7 -44,7 +44,7 @@@ private static String TAG = "InstantUploadService"; // TODO make it configurable over the settings dialog - public static String INSTANT_UPLOAD_DIR = "/InstantUpload"; + public static final String INSTANT_UPLOAD_DIR = "/InstantUpload"; private UploaderRunnable mUploaderRunnable; @Override @@@ -57,57 -57,53 +57,53 @@@ 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(); } - + String filename = intent.getStringExtra(KEY_DISPLAY_NAME); String filepath = intent.getStringExtra(KEY_FILE_PATH); 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(); } - - public void addElementToQueue(String filename, - String filepath, - String mimetype, - long length, - Account account) { + + public void addElementToQueue(String filename, String filepath, String mimetype, long length, Account account) { HashMap new_map = new HashMap(); new_map.put(KEY_ACCOUNT, account); new_map.put(KEY_DISPLAY_NAME, filename); new_map.put(KEY_FILE_PATH, filepath); new_map.put(KEY_MIME_TYPE, mimetype); new_map.put(KEY_FILE_SIZE, length); - + synchronized (mLock) { mHashMapList.add(new_map); } } - + private HashMap getFirstObject() { synchronized (mLock) { if (mHashMapList.size() == 0) @@@ -117,10 -113,10 +113,10 @@@ return ret; } } - + public void run() { HashMap working_map; - + while ((working_map = getFirstObject()) != null) { Account account = (Account) working_map.get(KEY_ACCOUNT); String filename = (String) working_map.get(KEY_DISPLAY_NAME); @@@ -128,15 -124,15 +124,15 @@@ String mimetype = (String) working_map.get(KEY_MIME_TYPE); WebdavClient wdc = OwnCloudClientUtils.createOwnCloudClient(account, getApplicationContext()); - - wdc.createDirectory(INSTANT_UPLOAD_DIR); // fail could just mean that it already exists; put will be tried anyway + + wdc.createDirectory(INSTANT_UPLOAD_DIR); // fail could just mean that it already exists put will be tried anyway try { - wdc.putFile(filepath, INSTANT_UPLOAD_DIR + "/" + filename, mimetype); + wdc.putFile(filepath, FileStorageUtils.getInstantUploadFilePath(filename), mimetype); } catch (Exception e) { // nothing to do; this service is deprecated, indeed } } } } - + } diff --combined 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; @@@ -63,19 -63,19 +64,19 @@@ public class ChunkedUploadFileOperatio File file = new File(getStoragePath()); raf = new RandomAccessFile(file, "r"); channel = raf.getChannel(); - ChunkFromFileChannelRequestEntity entity = new ChunkFromFileChannelRequestEntity(channel, getMimeType(), CHUNK_SIZE, file); - entity.addOnDatatransferProgressListeners(getDataTransferListeners()); + mEntity = new ChunkFromFileChannelRequestEntity(channel, getMimeType(), CHUNK_SIZE, file); + ((ProgressiveDataTransferer)mEntity).addDatatransferProgressListeners(getDataTransferListeners()); long offset = 0; String uriPrefix = client.getBaseUri() + WebdavUtils.encodePath(getRemotePath()) + "-chunking-" + Math.abs((new Random()).nextInt(9000)+1000) + "-" ; long chunkCount = (long) Math.ceil((double)file.length() / CHUNK_SIZE); for (int chunkIndex = 0; chunkIndex < chunkCount ; chunkIndex++, offset += CHUNK_SIZE) { 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 --combined src/com/owncloud/android/operations/DownloadFileOperation.java index e84345ca,da6b2701..0aa9d1e6 --- a/src/com/owncloud/android/operations/DownloadFileOperation.java +++ b/src/com/owncloud/android/operations/DownloadFileOperation.java @@@ -33,7 -33,6 +33,7 @@@ import org.apache.commons.httpclient.Ht import org.apache.commons.httpclient.methods.GetMethod; import org.apache.http.HttpStatus; +import com.owncloud.android.Log_OC; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.operations.RemoteOperation; import com.owncloud.android.operations.RemoteOperationResult; @@@ -105,7 -104,7 +105,7 @@@ public class DownloadFileOperation exte .getMimeTypeFromExtension( mFile.getRemotePath().substring(mFile.getRemotePath().lastIndexOf('.') + 1)); } catch (IndexOutOfBoundsException e) { - Log.e(TAG, "Trying to find out MIME type of a file without extension: " + mFile.getRemotePath()); + Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + mFile.getRemotePath()); } } if (mimeType == null) { @@@ -124,9 -123,17 +124,17 @@@ public void addDatatransferProgressListener (OnDatatransferProgressListener listener) { - mDataTransferListeners.add(listener); + synchronized (mDataTransferListeners) { + mDataTransferListeners.add(listener); + } } + public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) { + synchronized (mDataTransferListeners) { + mDataTransferListeners.remove(listener); + } + } + @Override protected RemoteOperationResult run(WebdavClient client) { RemoteOperationResult result = null; @@@ -149,11 -156,11 +157,11 @@@ result = new RemoteOperationResult(RemoteOperationResult.ResultCode.LOCAL_STORAGE_NOT_MOVED); else result = new RemoteOperationResult(isSuccess(status), status); - Log.i(TAG, "Download of " + mFile.getRemotePath() + " to " + getSavePath() + ": " + result.getLogMessage()); + Log_OC.i(TAG, "Download of " + mFile.getRemotePath() + " to " + getSavePath() + ": " + result.getLogMessage()); } catch (Exception e) { result = new RemoteOperationResult(e); - Log.e(TAG, "Download of " + mFile.getRemotePath() + " to " + getSavePath() + ": " + result.getLogMessage(), e); + Log_OC.e(TAG, "Download of " + mFile.getRemotePath() + " to " + getSavePath() + ": " + result.getLogMessage(), e); } return result; @@@ -191,9 -198,11 +199,11 @@@ } fos.write(bytes, 0, readResult); transferred += readResult; - it = mDataTransferListeners.iterator(); - while (it.hasNext()) { - it.next().onTransferProgress(readResult, transferred, mFile.getFileLength(), targetFile.getName()); + synchronized (mDataTransferListeners) { + it = mDataTransferListeners.iterator(); + while (it.hasNext()) { + it.next().onTransferProgress(readResult, transferred, mFile.getFileLength(), targetFile.getName()); + } } } savedFile = true; @@@ -222,4 -231,5 +232,5 @@@ mCancellationRequested.set(true); // atomic set; there is no need of synchronizing it } + } diff --combined 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 @@@ -28,7 -28,6 +28,7 @@@ import android.content.Context import android.content.Intent; import android.util.Log; +import com.owncloud.android.Log_OC; import com.owncloud.android.datamodel.DataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.files.services.FileDownloader; @@@ -159,11 -158,11 +159,11 @@@ public class SynchronizeFileOperation e } - Log.i(TAG, "Synchronizing " + mAccount.name + ", file " + mLocalFile.getRemotePath() + ": " + result.getLogMessage()); + Log_OC.i(TAG, "Synchronizing " + mAccount.name + ", file " + mLocalFile.getRemotePath() + ": " + result.getLogMessage()); } 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 --combined 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; @@@ -44,8 -48,6 +48,6 @@@ import eu.alefzero.webdav.FileRequestEn import eu.alefzero.webdav.OnDatatransferProgressListener; import eu.alefzero.webdav.WebdavClient; import eu.alefzero.webdav.WebdavUtils; - import android.accounts.Account; - import android.util.Log; /** * Remote operation performing the upload of a file to an ownCloud server @@@ -53,7 -55,7 +55,7 @@@ * @author David A. Velasco */ public class UploadFileOperation extends RemoteOperation { - + private static final String TAG = UploadFileOperation.class.getSimpleName(); private Account mAccount; @@@ -71,6 -73,8 +73,8 @@@ private Set mDataTransferListeners = new HashSet(); private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false); + protected RequestEntity mEntity = null; + public UploadFileOperation( Account account, OCFile file, @@@ -81,10 -85,13 +85,13 @@@ throw new IllegalArgumentException("Illegal NULL account in UploadFileOperation creation"); if (file == null) throw new IllegalArgumentException("Illegal NULL file in UploadFileOperation creation"); - if (file.getStoragePath() == null || file.getStoragePath().length() <= 0 || !(new File(file.getStoragePath()).exists())) { - throw new IllegalArgumentException("Illegal file in UploadFileOperation; storage path invalid or file not found: " + file.getStoragePath()); + if (file.getStoragePath() == null || file.getStoragePath().length() <= 0 + || !(new File(file.getStoragePath()).exists())) { + throw new IllegalArgumentException( + "Illegal file in UploadFileOperation; storage path invalid or file not found: " + + file.getStoragePath()); } - + mAccount = account; mFile = file; mRemotePath = file.getRemotePath(); @@@ -95,39 -102,38 +102,38 @@@ mOriginalFileName = mFile.getFileName(); } - public Account getAccount() { return mAccount; } - + public String getFileName() { return mOriginalFileName; } - + public OCFile getFile() { return mFile; } - + public OCFile getOldFile() { - return mOldFile; + return mOldFile; } - + public String getOriginalStoragePath() { return mOriginalStoragePath; } - + public String getStoragePath() { return mFile.getStoragePath(); } public String getRemotePath() { - return mFile.getRemotePath(); + return mFile.getRemotePath(); } public String getMimeType() { return mFile.getMimetype(); } - + public boolean isInstant() { return mIsInstant; } @@@ -135,7 -141,7 +141,7 @@@ public boolean isRemoteFolderToBeCreated() { return mRemoteFolderToBeCreated; } - + public void setRemoteFolderToBeCreated() { mRemoteFolderToBeCreated = true; } @@@ -143,50 -149,74 +149,74 @@@ public boolean getForceOverwrite() { return mForceOverwrite; } - + public boolean wasRenamed() { return mWasRenamed; } - + public Set getDataTransferListeners() { return mDataTransferListeners; } public void addDatatransferProgressListener (OnDatatransferProgressListener listener) { - mDataTransferListeners.add(listener); + synchronized (mDataTransferListeners) { + mDataTransferListeners.add(listener); + } + if (mEntity != null) { + ((ProgressiveDataTransferer)mEntity).addDatatransferProgressListener(listener); + } } + public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) { + synchronized (mDataTransferListeners) { + mDataTransferListeners.remove(listener); + } + if (mEntity != null) { + ((ProgressiveDataTransferer)mEntity).removeDatatransferProgressListener(listener); + } + } + @Override protected RemoteOperationResult run(WebdavClient client) { RemoteOperationResult result = null; boolean localCopyPassed = false, nameCheckPassed = false; File temporalFile = null, originalFile = new File(mOriginalStoragePath), expectedFile = null; try { - /// rename the file to upload, if necessary + // / rename the file to upload, if necessary if (!mForceOverwrite) { String remotePath = getAvailableRemotePath(client, mRemotePath); mWasRenamed = !remotePath.equals(mRemotePath); if (mWasRenamed) { - createNewOCFile(remotePath); + createNewOCFile(remotePath); } } nameCheckPassed = true; - - String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile); /// not before getAvailableRemotePath() !!! + + String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile); // / + // not + // before + // getAvailableRemotePath() + // !!! expectedFile = new File(expectedPath); - - /// check location of local file; if not the expected, copy to a temporal file before upload (if COPY is the expected behaviour) + + // / check location of local file; if not the expected, copy to a + // temporal file before upload (if COPY is the expected behaviour) if (!mOriginalStoragePath.equals(expectedPath) && mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_COPY) { if (FileStorageUtils.getUsableSpace(mAccount.name) < originalFile.length()) { result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_FULL); - return result; // error condition when the file should be copied - + return result; // error condition when the file should be + // copied + } else { String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath(); mFile.setStoragePath(temporalPath); temporalFile = new File(temporalPath); - if (!mOriginalStoragePath.equals(temporalPath)) { // preventing weird but possible situation + if (!mOriginalStoragePath.equals(temporalPath)) { // preventing + // weird + // but + // possible + // situation InputStream in = null; OutputStream out = null; try { @@@ -198,38 -228,42 +228,40 @@@ temporalFile.createNewFile(); if (!temporalFile.isFile()) { throw new IOException("Unexpected error: target file could not be created"); - } + } in = new FileInputStream(originalFile); out = new FileOutputStream(temporalFile); byte[] buf = new byte[1024]; int len; - while ((len = in.read(buf)) > 0){ + while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); } - + } 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); } } } } } localCopyPassed = true; - - /// perform the upload - synchronized(mCancellationRequested) { + + // / perform the upload + synchronized (mCancellationRequested) { if (mCancellationRequested.get()) { throw new OperationCancelledException(); } else { @@@ -237,19 -271,21 +269,21 @@@ } } int status = uploadFile(client); - - - /// move local temporal file or original file to its corresponding location in the ownCloud local folder + + // / move local temporal file or original file to its corresponding + // location in the ownCloud local folder if (isSuccess(status)) { if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_FORGET) { mFile.setStoragePath(null); - + } else { mFile.setStoragePath(expectedPath); File fileToMove = null; - if (temporalFile != null) { // FileUploader.LOCAL_BEHAVIOUR_COPY ; see where temporalFile was set + if (temporalFile != null) { // FileUploader.LOCAL_BEHAVIOUR_COPY + // ; see where temporalFile was + // set fileToMove = temporalFile; - } else { // FileUploader.LOCAL_BEHAVIOUR_MOVE + } else { // FileUploader.LOCAL_BEHAVIOUR_MOVE fileToMove = originalFile; } if (!expectedFile.equals(fileToMove)) { @@@ -257,18 -293,22 +291,22 @@@ expectedFolder.mkdirs(); if (!expectedFolder.isDirectory() || !fileToMove.renameTo(expectedFile)) { mFile.setStoragePath(null); // forget the local file - // by now, treat this as a success; the file was uploaded; the user won't like that the local file is not linked, but this should be a veeery rare fail; - // the best option could be show a warning message (but not a fail) - //result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_MOVED); - //return result; + // by now, treat this as a success; the file was + // uploaded; the user won't like that the local file + // is not linked, but this should be a very rare + // fail; + // the best option could be show a warning message + // (but not a fail) + // result = new + // RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_MOVED); + // return result; } } - } + } } - + result = new RemoteOperationResult(isSuccess(status), status); - - + } catch (Exception e) { // TODO something cleaner with cancellations if (mCancellationRequested.get()) { @@@ -276,34 -316,36 +314,32 @@@ } else { result = new RemoteOperationResult(e); } - - + } finally { if (temporalFile != null && !originalFile.equals(temporalFile)) { 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()); } } } - + return result; } - private void createNewOCFile(String newRemotePath) { // a new OCFile instance must be created for a new remote path OCFile newFile = new OCFile(newRemotePath); @@@ -322,31 -364,32 +358,32 @@@ mFile = newFile; } - public boolean isSuccess(int status) { return ((status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED || status == HttpStatus.SC_NO_CONTENT)); } - - + protected int uploadFile(WebdavClient client) throws HttpException, IOException, OperationCancelledException { int status = -1; try { File f = new File(mFile.getStoragePath()); - FileRequestEntity entity = new FileRequestEntity(f, getMimeType()); - entity.addOnDatatransferProgressListeners(mDataTransferListeners); - mPutMethod.setRequestEntity(entity); + mEntity = new FileRequestEntity(f, getMimeType()); + synchronized (mDataTransferListeners) { + ((ProgressiveDataTransferer)mEntity).addDatatransferProgressListeners(mDataTransferListeners); + } + mPutMethod.setRequestEntity(mEntity); status = client.executeMethod(mPutMethod); client.exhaustResponse(mPutMethod.getResponseBodyAsStream()); - + } finally { - mPutMethod.releaseConnection(); // let the connection available for other methods + mPutMethod.releaseConnection(); // let the connection available for + // other methods } return status; } - + /** - * Checks if remotePath does not exist in the server and returns it, or adds a suffix to it in order to avoid the server - * file is overwritten. + * Checks if remotePath does not exist in the server and returns it, or adds + * a suffix to it in order to avoid the server file is overwritten. * * @param string * @return @@@ -356,12 -399,12 +393,12 @@@ if (!check) { return remotePath; } - + int pos = remotePath.lastIndexOf("."); String suffix = ""; String extension = ""; if (pos >= 0) { - extension = remotePath.substring(pos+1); + extension = remotePath.substring(pos + 1); remotePath = remotePath.substring(0, pos); } int count = 2; @@@ -374,21 -417,19 +411,19 @@@ count++; } while (check); - if (pos >=0) { + if (pos >= 0) { return remotePath + suffix + "." + extension; } else { return remotePath + suffix; } } - public void cancel() { - synchronized(mCancellationRequested) { + synchronized (mCancellationRequested) { mCancellationRequested.set(true); if (mPutMethod != null) mPutMethod.abort(); } } - } diff --combined 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 --combined 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 @@@ -4,7 -4,7 +4,7 @@@ * * 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 + * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, @@@ -23,9 -23,9 +23,9 @@@ import java.io.File import android.accounts.Account; import android.app.AlertDialog; + import android.app.ProgressDialog; import android.app.AlertDialog.Builder; import android.app.Dialog; - import android.app.ProgressDialog; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@@ -49,6 -49,7 +49,7 @@@ import android.os.Handler import android.os.IBinder; import android.preference.PreferenceManager; import android.provider.MediaStore; + import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; import android.util.Log; import android.view.View; @@@ -66,8 -67,6 +67,8 @@@ import com.actionbarsherlock.view.MenuI import com.actionbarsherlock.view.MenuItem; import com.actionbarsherlock.view.Window; import com.owncloud.android.AccountUtils; +import com.owncloud.android.Log_OC; +import com.owncloud.android.R; import com.owncloud.android.authenticator.AccountAuthenticator; import com.owncloud.android.datamodel.DataStorageManager; import com.owncloud.android.datamodel.FileDataStorageManager; @@@ -81,29 -80,36 +82,36 @@@ import com.owncloud.android.network.Own 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.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.syncadapter.FileSyncService; import com.owncloud.android.ui.dialog.ChangelogDialog; + import com.owncloud.android.ui.dialog.EditNameDialog; import com.owncloud.android.ui.dialog.SslValidatorDialog; + import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener; import com.owncloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener; import com.owncloud.android.ui.fragment.FileDetailFragment; + import com.owncloud.android.ui.fragment.FileFragment; import com.owncloud.android.ui.fragment.OCFileListFragment; + import com.owncloud.android.ui.preview.PreviewImageActivity; + import com.owncloud.android.ui.preview.PreviewImageFragment; + import com.owncloud.android.ui.preview.PreviewMediaFragment; + import com.owncloud.android.R; import eu.alefzero.webdav.WebdavClient; /** * Displays, what files the user has available in his ownCloud. * * @author Bartek Przybylski - * + * @author David A. Velasco */ - public class FileDisplayActivity extends SherlockFragmentActivity implements OCFileListFragment.ContainerActivity, - FileDetailFragment.ContainerActivity, OnNavigationListener, OnSslValidatorListener, OnRemoteOperationListener { - + public class FileDisplayActivity extends SherlockFragmentActivity implements + OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNavigationListener, OnSslValidatorListener, OnRemoteOperationListener, EditNameDialogListener { + private ArrayAdapter mDirectories; private OCFile mCurrentDir = null; private OCFile mCurrentFile = null; @@@ -120,9 -126,11 +128,10 @@@ private OCFileListFragment mFileList; private boolean mDualPane; + private boolean mBackFromCreatingFirstAccount; private static final int DIALOG_SETUP_ACCOUNT = 0; private static final int DIALOG_CREATE_DIR = 1; - private static final int DIALOG_ABOUT_APP = 2; public static final int DIALOG_SHORT_WAIT = 3; private static final int DIALOG_CHOOSE_UPLOAD_SOURCE = 4; private static final int DIALOG_SSL_VALIDATOR = 5; @@@ -132,39 -140,45 +141,44 @@@ 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 - mCurrentDir = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE); // no check necessary, mCurrenDir == null if the parameter is not in the intent Account account = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_ACCOUNT); - if (account != null) - AccountUtils.setCurrentOwnCloudAccount(this, account.name); + if (account != null && AccountUtils.setCurrentOwnCloudAccount(this, account.name)) { + mCurrentDir = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE); + } /// Load of saved instance state: keep this always before initDataFromCurrentAccount() if(savedInstanceState != null) { // TODO - test if savedInstanceState should take precedence over file in the intent ALWAYS (now), NEVER, or SOME TIMES mCurrentDir = savedInstanceState.getParcelable(FileDetailFragment.EXTRA_FILE); + mWaitingToPreview = (OCFile) savedInstanceState.getParcelable(FileDetailActivity.KEY_WAITING_TO_PREVIEW); + + } else { + mWaitingToPreview = null; } if (!AccountUtils.accountsAreSetup(this)) { /// no account available: FORCE ACCOUNT CREATION mStorageManager = null; createFirstAccount(); - - } else { // / at least an account is available - - initDataFromCurrentAccount(); // it checks mCurrentDir and - // mCurrentFile with the current - // account - + + } else { /// at least an account is available + + initDataFromCurrentAccount(); // it checks mCurrentDir and mCurrentFile with the current account + } mUploadConnection = new ListServiceConnection(); @@@ -189,7 -203,7 +203,7 @@@ // Drop-down navigation mDirectories = new CustomArrayAdapter(this, R.layout.sherlock_spinner_dropdown_item); OCFile currFile = mCurrentDir; - while (currFile != null && currFile.getFileName() != OCFile.PATH_SEPARATOR) { + while(mStorageManager != null && currFile != null && currFile.getFileName() != OCFile.PATH_SEPARATOR) { mDirectories.add(currFile.getFileName()); currFile = mStorageManager.getFileById(currFile.getParentId()); } @@@ -200,7 -214,20 +214,20 @@@ mFileList = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList); mDualPane = (findViewById(R.id.file_details_container) != null); if (mDualPane) { - initFileDetailsInDualPane(); + if (savedInstanceState == null) initFileDetailsInDualPane(); + } else { + // quick patchES to fix problem in turn from landscape to portrait, when a file is selected in the right pane + // TODO serious refactorization in activities and fragments providing file browsing and handling + if (mCurrentFile != null) { + onFileClick(mCurrentFile); + mCurrentFile = null; + } + Fragment rightPanel = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG); + if (rightPanel != null) { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.remove(rightPanel); + transaction.commit(); + } } // Action bar setup @@@ -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"); } @@@ -255,7 -283,7 +283,7 @@@ startActivity(intent); // the new activity won't be created until this.onStart() and this.onResume() are finished; } - + /** * Load of state dependent of the existence of an ownCloud account */ @@@ -292,13 -320,20 +320,20 @@@ if (mDualPane && getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG) == null) { FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); if (mCurrentFile != null) { - transaction.replace(R.id.file_details_container, - new FileDetailFragment(mCurrentFile, AccountUtils.getCurrentOwnCloudAccount(this)), - FileDetailFragment.FTAG); // empty FileDetailFragment + if (PreviewMediaFragment.canBePreviewed(mCurrentFile)) { + if (mCurrentFile.isDown()) { + transaction.replace(R.id.file_details_container, new PreviewMediaFragment(mCurrentFile, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG); + } else { + transaction.replace(R.id.file_details_container, new FileDetailFragment(mCurrentFile, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG); + mWaitingToPreview = mCurrentFile; + } + } else { + transaction.replace(R.id.file_details_container, new FileDetailFragment(mCurrentFile, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG); + } mCurrentFile = null; + } else { - transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null), - FileDetailFragment.FTAG); // empty FileDetailFragment + transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null), FileDetailFragment.FTAG); // empty FileDetailFragment } transaction.commit(); } @@@ -318,39 -353,67 +353,41 @@@ @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; } - /** - * Workaround for this: http://code.google.com/p/android/issues/detail?id=3974 - * - * @param menu Menu to patch - */ - private void patchHiddenAccents(Menu menu) { - for (int i = 0; i < mMenuIdentifiersToPatch.length ; i++) { - MenuItem aboutItem = menu.findItem(mMenuIdentifiersToPatch[i]); - if (aboutItem != null && aboutItem.getIcon() instanceof BitmapDrawable) { - // Clip off the bottom three (density independent) pixels of transparent padding - Bitmap original = ((BitmapDrawable) aboutItem.getIcon()).getBitmap(); - float scale = getResources().getDisplayMetrics().density; - int clippedHeight = (int) (original.getHeight() - (3 * scale)); - Bitmap scaled = Bitmap.createBitmap(original, 0, 0, original.getWidth(), clippedHeight); - aboutItem.setIcon(new BitmapDrawable(getResources(), scaled)); - } - } - } - - @Override 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; } @@@ -383,13 -446,11 +420,11 @@@ * Called, when the user selected something for uploading */ public void onActivityResult(int requestCode, int resultCode, Intent data) { - - if (requestCode == ACTION_SELECT_CONTENT_FROM_APPS - && (resultCode == RESULT_OK || resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)) { + + if (requestCode == ACTION_SELECT_CONTENT_FROM_APPS && (resultCode == RESULT_OK || resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)) { requestSimpleUpload(data, resultCode); - - } else if (requestCode == ACTION_SELECT_MULTIPLE_FILES - && (resultCode == RESULT_OK || resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)) { + + } else if (requestCode == ACTION_SELECT_MULTIPLE_FILES && (resultCode == RESULT_OK || resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)) { requestMultipleUpload(data, resultCode); } @@@ -405,7 -466,7 +440,7 @@@ } if (!remotePathBase.endsWith(OCFile.PATH_SEPARATOR)) remotePathBase += OCFile.PATH_SEPARATOR; - for (int j = 0; j < remotePaths.length; j++) { + for (int j = 0; j< remotePaths.length; j++) { remotePaths[j] = remotePathBase + (new File(filePaths[j])).getName(); } @@@ -419,7 -480,7 +454,7 @@@ startService(i); } else { - Log.d("FileDisplay", "User clicked on 'Update' with no selection"); + Log_OC.d("FileDisplay", "User clicked on 'Update' with no selection"); Toast t = Toast.makeText(this, getString(R.string.filedisplay_no_file_selected), Toast.LENGTH_LONG); t.show(); return; @@@ -441,12 -502,12 +476,12 @@@ filepath = filemanagerstring; } catch (Exception e) { - Log.e("FileDisplay", "Unexpected exception when trying to read the result of Intent.ACTION_GET_CONTENT", e); + Log_OC.e("FileDisplay", "Unexpected exception when trying to read the result of Intent.ACTION_GET_CONTENT", e); e.printStackTrace(); } finally { if (filepath == null) { - Log.e("FileDisplay", "Couldnt resolve path to file"); + Log_OC.e("FileDisplay", "Couldnt resolve path to file"); Toast t = Toast.makeText(this, getString(R.string.filedisplay_unexpected_bad_get_content), Toast.LENGTH_LONG); t.show(); return; @@@ -454,7 -515,8 +489,8 @@@ } Intent i = new Intent(this, FileUploader.class); - i.putExtra(FileUploader.KEY_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this)); + i.putExtra(FileUploader.KEY_ACCOUNT, + AccountUtils.getCurrentOwnCloudAccount(this)); String remotepath = new String(); for (int j = mDirectories.getCount() - 2; j >= 0; --j) { remotepath += OCFile.PATH_SEPARATOR + mDirectories.getItem(j); @@@ -483,46 -545,44 +519,44 @@@ mCurrentDir = mFileList.getCurrentFile(); if (mDualPane) { - // Resets the FileDetailsFragment on Tablets so that it always - // displays - FileDetailFragment fileDetails = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag( - FileDetailFragment.FTAG); - if (fileDetails != null && !fileDetails.isEmpty()) { + // Resets the FileDetailsFragment on Tablets so that it always displays + Fragment fileFragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG); + if (fileFragment != null && (fileFragment instanceof PreviewMediaFragment || !((FileDetailFragment) fileFragment).isEmpty())) { FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.remove(fileDetails); - transaction.add(R.id.file_details_container, new FileDetailFragment(null, null), - FileDetailFragment.FTAG); + transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null), FileDetailFragment.FTAG); // empty FileDetailFragment transaction.commit(); } } - - if (mCurrentDir.getParentId() == 0) { - ActionBar actionBar = getSupportActionBar(); + + if(mCurrentDir.getParentId() == 0){ + ActionBar actionBar = getSupportActionBar(); actionBar.setDisplayHomeAsUpEnabled(false); - } + } } @Override protected void onSaveInstanceState(Bundle outState) { // responsibility of restore is preferred in onCreate() before than in onRestoreInstanceState when there are Fragments involved - Log.d(getClass().toString(), "onSaveInstanceState() start"); + Log_OC.d(getClass().toString(), "onSaveInstanceState() start"); super.onSaveInstanceState(outState); outState.putParcelable(FileDetailFragment.EXTRA_FILE, mCurrentDir); if (mDualPane) { - FileDetailFragment fragment = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG); + FileFragment fragment = (FileFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG); if (fragment != null) { - OCFile file = fragment.getDisplayedFile(); + OCFile file = fragment.getFile(); if (file != null) { outState.putParcelable(FileDetailFragment.EXTRA_FILE, file); } } } + 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)) { @@@ -533,6 -593,7 +567,7 @@@ if (mDualPane) { initFileDetailsInDualPane(); } + mBackFromCreatingFirstAccount = true; } // Listen for sync messages @@@ -546,7 -607,8 +581,8 @@@ registerReceiver(mUploadFinishReceiver, uploadIntentFilter); // Listen for download messages - IntentFilter downloadIntentFilter = new IntentFilter(FileDownloader.DOWNLOAD_FINISH_MESSAGE); + IntentFilter downloadIntentFilter = new IntentFilter(FileDownloader.DOWNLOAD_ADDED_MESSAGE); + downloadIntentFilter.addAction(FileDownloader.DOWNLOAD_FINISH_MESSAGE); mDownloadFinishReceiver = new DownloadFinishReceiver(); registerReceiver(mDownloadFinishReceiver, downloadIntentFilter); @@@ -559,13 -621,13 +595,13 @@@ showDialog(DIALOG_SETUP_ACCOUNT); } - Log.d(getClass().toString(), "onResume() end"); + Log_OC.d(getClass().toString(), "onResume() end"); } @Override - public void onPause() { - Log.d(getClass().toString(), "onPause() start"); + protected void onPause() { + Log_OC.d(getClass().toString(), "onPause() start"); super.onPause(); if (mSyncBroadcastReceiver != null) { unregisterReceiver(mSyncBroadcastReceiver); @@@ -583,7 -645,7 +619,7 @@@ dismissDialog(DIALOG_SETUP_ACCOUNT); } - Log.d(getClass().toString(), "onPause() end"); + Log_OC.d(getClass().toString(), "onPause() end"); } @@@ -622,6 -684,22 +658,6 @@@ dialog = builder.create(); break; } - case DIALOG_ABOUT_APP: { - builder = new AlertDialog.Builder(this); - builder.setTitle(getString(R.string.about_title)); - PackageInfo pkg; - try { - pkg = getPackageManager().getPackageInfo(getPackageName(), 0); - builder.setMessage(String.format(getString(R.string.about_message), getString(R.string.app_name), pkg.versionName)); - builder.setIcon(android.R.drawable.ic_menu_info_details); - dialog = builder.create(); - } catch (NameNotFoundException e) { - builder = null; - dialog = null; - Log.e(TAG, "Error while showing about dialog", e); - } - break; - } case DIALOG_CREATE_DIR: { builder = new Builder(this); final EditText dirNameInput = new EditText(getBaseContext()); @@@ -629,57 -707,71 +665,71 @@@ builder.setTitle(R.string.uploader_info_dirname); int typed_color = getResources().getColor(R.color.setup_text_typed); dirNameInput.setTextColor(typed_color); - builder.setPositiveButton(android.R.string.ok, new OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - String directoryName = dirNameInput.getText().toString(); - if (directoryName.trim().length() == 0) { - dialog.cancel(); - return; - } - - // Figure out the path where the dir needs to be created - String path; - if (mCurrentDir == null) { - // this is just a patch; we should ensure that - // mCurrentDir never is null - if (!mStorageManager.fileExists(OCFile.PATH_SEPARATOR)) { - OCFile file = new OCFile(OCFile.PATH_SEPARATOR); - mStorageManager.saveFile(file); + builder.setPositiveButton(android.R.string.ok, + new OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + String directoryName = dirNameInput.getText().toString(); + if (directoryName.trim().length() == 0) { + dialog.cancel(); + return; + } + + // Figure out the path where the dir needs to be created + String path; + if (mCurrentDir == null) { + // this is just a patch; we should ensure that mCurrentDir never is null + if (!mStorageManager.fileExists(OCFile.PATH_SEPARATOR)) { + OCFile file = new OCFile(OCFile.PATH_SEPARATOR); + mStorageManager.saveFile(file); + } + mCurrentDir = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR); + } + path = FileDisplayActivity.this.mCurrentDir.getRemotePath(); + + // Create directory + path += directoryName + OCFile.PATH_SEPARATOR; + Thread thread = new Thread(new DirectoryCreator(path, AccountUtils.getCurrentOwnCloudAccount(FileDisplayActivity.this), new Handler())); + thread.start(); + + dialog.dismiss(); + + showDialog(DIALOG_SHORT_WAIT); } - mCurrentDir = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR); - } - path = FileDisplayActivity.this.mCurrentDir.getRemotePath(); - - // Create directory - path += directoryName + OCFile.PATH_SEPARATOR; - Thread thread = new Thread(new DirectoryCreator(path, AccountUtils - .getCurrentOwnCloudAccount(FileDisplayActivity.this), new Handler())); - thread.start(); - - dialog.dismiss(); - - showDialog(DIALOG_SHORT_WAIT); - } - }); - builder.setNegativeButton(R.string.common_cancel, new OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dialog.cancel(); - } - }); + }); + builder.setNegativeButton(R.string.common_cancel, + new OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.cancel(); + } + }); dialog = builder.create(); break; } case DIALOG_SHORT_WAIT: { ProgressDialog working_dialog = new ProgressDialog(this); - working_dialog.setMessage(getResources().getString(R.string.wait_a_moment)); + working_dialog.setMessage(getResources().getString( + R.string.wait_a_moment)); working_dialog.setIndeterminate(true); working_dialog.setCancelable(false); dialog = working_dialog; break; } case DIALOG_CHOOSE_UPLOAD_SOURCE: { - final String[] items = { getString(R.string.actionbar_upload_files), - getString(R.string.actionbar_upload_from_apps), getString(R.string.actionbar_failed_instant_upload) }; + + String[] items = null; + + String[] allTheItems = { getString(R.string.actionbar_upload_files), + getString(R.string.actionbar_upload_from_apps), + getString(R.string.actionbar_failed_instant_upload) }; + + String[] commonItems = { getString(R.string.actionbar_upload_files), + getString(R.string.actionbar_upload_from_apps) }; + + if (InstantUploadActivity.IS_ENABLED) + items = allTheItems; + else + items = commonItems; + builder = new AlertDialog.Builder(this); builder.setTitle(R.string.actionbar_upload); builder.setItems(items, new DialogInterface.OnClickListener() { @@@ -699,7 -791,7 +749,7 @@@ action = action.setType("*/*").addCategory(Intent.CATEGORY_OPENABLE); startActivityForResult(Intent.createChooser(action, getString(R.string.upload_chooser_title)), ACTION_SELECT_CONTENT_FROM_APPS); - } else if (item == 2) { + } else if (item == 2 && InstantUploadActivity.IS_ENABLED) { Account account = AccountUtils.getCurrentOwnCloudAccount(FileDisplayActivity.this); Intent action = new Intent(FileDisplayActivity.this, InstantUploadActivity.class); action.putExtra(FileUploader.KEY_ACCOUNT, account); @@@ -719,11 -811,11 +769,11 @@@ builder.setMessage(getResources().getString(R.string.ssl_validator_not_saved)); builder.setCancelable(false); builder.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - }; - }); + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + }; + }); dialog = builder.create(); break; } @@@ -745,10 -837,11 +795,11 @@@ String[] projection = { MediaStore.Images.Media.DATA }; Cursor cursor = managedQuery(uri, projection, null, null, null); if (cursor != null) { - int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); + int column_index = cursor + .getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); return cursor.getString(column_index); - } + } return null; } @@@ -812,12 -905,11 +863,11 @@@ public void run() { dismissDialog(DIALOG_SHORT_WAIT); try { - Toast msg = Toast.makeText(FileDisplayActivity.this, R.string.create_dir_fail_msg, - Toast.LENGTH_LONG); + Toast msg = Toast.makeText(FileDisplayActivity.this, R.string.create_dir_fail_msg, Toast.LENGTH_LONG); msg.show(); } catch (NotFoundException e) { - Log.e(TAG, "Error while trying to show fail message " , e); + Log_OC.e(TAG, "Error while trying to show fail message ", e); } } }); @@@ -835,16 -927,19 +885,19 @@@ public View getView(int position, View convertView, ViewGroup parent) { View v = super.getView(position, convertView, parent); - - ((TextView) v).setTextColor(getResources().getColorStateList(android.R.color.white)); + + ((TextView) v).setTextColor(getResources().getColorStateList( + android.R.color.white)); return v; } - - public View getDropDownView(int position, View convertView, ViewGroup parent) { + + public View getDropDownView(int position, View convertView, + ViewGroup parent) { View v = super.getDropDownView(position, convertView, parent); - - ((TextView) v).setTextColor(getResources().getColorStateList(android.R.color.white)); - + + ((TextView) v).setTextColor(getResources().getColorStateList( + android.R.color.white)); + return v; } @@@ -857,24 -952,27 +910,24 @@@ */ @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("/"); fillBlankRoot = (mCurrentDir != null); } - if ((synchFolderRemotePath != null && mCurrentDir != null && (mCurrentDir.getRemotePath() - .equals(synchFolderRemotePath))) || fillBlankRoot) { - if (!fillBlankRoot) + if ((synchFolderRemotePath != null && mCurrentDir != null && (mCurrentDir.getRemotePath().equals(synchFolderRemotePath))) + || fillBlankRoot ) { + if (!fillBlankRoot) mCurrentDir = getStorageManager().getFileByPath(synchFolderRemotePath); OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager() .findFragmentById(R.id.fileList); @@@ -884,16 -982,22 +937,22 @@@ } setSupportProgressBarIndeterminateVisibility(inProgress); + if (mBackFromCreatingFirstAccount) { + // awful patch to fix problem with visibility of progress circle with the first refresh of the first account + // TODO - kill this Activity when the first account has to be created instead of stack the account creation on it + getSupportActionBar().hide(); + getSupportActionBar().show(); + mBackFromCreatingFirstAccount = false; + } removeStickyBroadcast(intent); } - - RemoteOperationResult synchResult = (RemoteOperationResult) intent - .getSerializableExtra(FileSyncService.SYNC_RESULT); + + RemoteOperationResult synchResult = (RemoteOperationResult)intent.getSerializableExtra(FileSyncService.SYNC_RESULT); if (synchResult != null) { if (synchResult.getCode().equals(RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED)) { mLastSslUntrustedServerResult = synchResult; - showDialog(DIALOG_SSL_VALIDATOR); + showDialog(DIALOG_SSL_VALIDATOR); } } } @@@ -911,12 -1015,10 +970,10 @@@ String uploadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH); String accountName = intent.getStringExtra(FileUploader.ACCOUNT_NAME); boolean sameAccount = accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name); - boolean isDescendant = (mCurrentDir != null) && (uploadedRemotePath != null) - && (uploadedRemotePath.startsWith(mCurrentDir.getRemotePath())); + boolean isDescendant = (mCurrentDir != null) && (uploadedRemotePath != null) && (uploadedRemotePath.startsWith(mCurrentDir.getRemotePath())); if (sameAccount && isDescendant) { - OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager() - .findFragmentById(R.id.fileList); - if (fileListFragment != null) { + OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList); + if (fileListFragment != null) { fileListFragment.listDirectory(); } } @@@ -926,29 -1028,78 +983,78 @@@ /** - * Once the file download has finished -> update view + * 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 folder. */ 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); - String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME); - boolean sameAccount = accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name); - boolean isDescendant = (mCurrentDir != null) && (downloadedRemotePath != null) - && (downloadedRemotePath.startsWith(mCurrentDir.getRemotePath())); + boolean isDescendant = isDescendant(downloadedRemotePath); + if (sameAccount && isDescendant) { - OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager() - .findFragmentById(R.id.fileList); - if (fileListFragment != null) { - fileListFragment.listDirectory(); + updateLeftPanel(); + if (mDualPane) { + updateRightPanel(intent.getAction(), downloadedRemotePath, intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false)); } } + + removeStickyBroadcast(intent); + } + + private boolean isDescendant(String downloadedRemotePath) { + return (mCurrentDir != null && downloadedRemotePath != null && downloadedRemotePath.startsWith(mCurrentDir.getRemotePath())); + } + + private boolean isSameAccount(Context context, Intent intent) { + String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME); + return (accountName != null && accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name)); } } - - + protected void updateLeftPanel() { + OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList); + if (fileListFragment != null) { + fileListFragment.listDirectory(); + } + } + + protected void updateRightPanel(String downloadEvent, String downloadedRemotePath, boolean success) { + Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG); + boolean waitedPreview = (mWaitingToPreview != null && mWaitingToPreview.getRemotePath().equals(downloadedRemotePath)); + if (fragment != null && fragment instanceof FileDetailFragment) { + FileDetailFragment detailsFragment = (FileDetailFragment) fragment; + OCFile fileInFragment = detailsFragment.getFile(); + if (fileInFragment != null && !downloadedRemotePath.equals(fileInFragment.getRemotePath())) { + // the user browsed to other file ; forget the automatic preview + mWaitingToPreview = null; + + } else if (downloadEvent.equals(FileDownloader.DOWNLOAD_ADDED_MESSAGE)) { + // grant that the right panel updates the progress bar + detailsFragment.listenForTransferProgress(); + detailsFragment.updateFileDetails(true, false); + + } else if (downloadEvent.equals(FileDownloader.DOWNLOAD_FINISH_MESSAGE)) { + // update the right panel + if (success && waitedPreview) { + mWaitingToPreview = mStorageManager.getFileById(mWaitingToPreview.getFileId()); // update the file from database, for the local storage path + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.replace(R.id.file_details_container, new PreviewMediaFragment(mWaitingToPreview, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG); + transaction.commit(); + mWaitingToPreview = null; + + } else { + detailsFragment.updateFileDetails(false, (success)); + } + } + } + } + + /** * {@inheritDoc} */ @@@ -957,7 -1108,7 +1063,7 @@@ return mStorageManager; } - + /** * {@inheritDoc} */ @@@ -969,12 -1120,10 +1075,10 @@@ if (mDualPane) { // Resets the FileDetailsFragment on Tablets so that it always displays - FileDetailFragment fileDetails = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG); - if (fileDetails != null && !fileDetails.isEmpty()) { + Fragment fileFragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG); + if (fileFragment != null && (fileFragment instanceof PreviewMediaFragment || !((FileDetailFragment) fileFragment).isEmpty())) { FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.remove(fileDetails); - transaction.add(R.id.file_details_container, new FileDetailFragment(null, null), - FileDetailFragment.FTAG); + transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null), FileDetailFragment.FTAG); // empty FileDetailFragment transaction.commit(); } } @@@ -986,19 -1135,40 +1090,40 @@@ */ @Override public void onFileClick(OCFile file) { - - // If we are on a large device -> update fragment + if (file != null && PreviewImageFragment.canBePreviewed(file)) { + // preview image - it handles the download, if needed + startPreviewImage(file); + + } else if (file != null && PreviewMediaFragment.canBePreviewed(file)) { + if (file.isDown()) { + // general preview + startMediaPreview(file); + + } else { + // automatic download, preview on finish + startDownloadForPreview(file); + + } + } else { + // details view + startDetails(file); + } + } + + private void startPreviewImage(OCFile file) { + Intent showDetailsIntent = new Intent(this, PreviewImageActivity.class); + showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file); + showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this)); + startActivity(showDetailsIntent); + } + + private void startMediaPreview(OCFile file) { if (mDualPane) { - // buttons in the details view are problematic when trying to reuse an existing fragment; create always a new one solves some of them, BUT no all; downloads are 'dangerous' FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction - .replace(R.id.file_details_container, - new FileDetailFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), - FileDetailFragment.FTAG); - transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); + transaction.replace(R.id.file_details_container, new PreviewMediaFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG); transaction.commit(); - - } else { // small or medium screen device -> new Activity + + } else { Intent showDetailsIntent = new Intent(this, FileDetailActivity.class); showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file); showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this)); @@@ -1006,7 -1176,37 +1131,37 @@@ } } + private void startDownloadForPreview(OCFile file) { + if (mDualPane) { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.replace(R.id.file_details_container, new FileDetailFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG); + transaction.commit(); + mWaitingToPreview = file; + requestForDownload(); + + } else { + Intent showDetailsIntent = new Intent(this, FileDetailActivity.class); + showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file); + showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this)); + startActivity(showDetailsIntent); + } + } + + private void startDetails(OCFile file) { + if (mDualPane && !file.isImage()) { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.replace(R.id.file_details_container, new FileDetailFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG); + transaction.commit(); + } else { + Intent showDetailsIntent = new Intent(this, FileDetailActivity.class); + showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file); + showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this)); + startActivity(showDetailsIntent); + } + } + + /** * {@inheritDoc} */ @@@ -1021,9 -1221,8 +1176,8 @@@ */ @Override public void onFileStateChanged() { - OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById( - R.id.fileList); - if (fileListFragment != null) { + OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList); + if (fileListFragment != null) { fileListFragment.listDirectory(); } } @@@ -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; @@@ -1065,20 -1268,22 +1223,22 @@@ if (mFileList != null) mFileList.listDirectory(); if (mDualPane) { - FileDetailFragment fragment = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag( - FileDetailFragment.FTAG); - if (fragment != null) - fragment.updateFileDetails(false); + Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG); + if (fragment != null && fragment instanceof FileDetailFragment) { + FileDetailFragment detailFragment = (FileDetailFragment)fragment; + detailFragment.listenForTransferProgress(); + detailFragment.updateFileDetails(false, false); + } } } @Override public void onServiceDisconnected(ComponentName component) { if (component.equals(new ComponentName(FileDisplayActivity.this, FileDownloader.class))) { - Log.d(TAG, "Download service disconnected"); + Log_OC.d(TAG, "Download service disconnected"); mDownloaderBinder = null; } else if (component.equals(new ComponentName(FileDisplayActivity.this, FileUploader.class))) { - Log.d(TAG, "Upload service disconnected"); + Log_OC.d(TAG, "Upload service disconnected"); mUploaderBinder = null; } } @@@ -1106,6 -1311,7 +1266,7 @@@ startSynchronization(); } + @Override public void onFailedSavingCertificate() { showDialog(DIALOG_CERT_NOT_SAVED); @@@ -1122,13 -1328,13 +1283,13 @@@ @Override public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { if (operation instanceof RemoveFileOperation) { - onRemoveFileOperationFinish((RemoveFileOperation) operation, result); - + onRemoveFileOperationFinish((RemoveFileOperation)operation, result); + } else if (operation instanceof RenameFileOperation) { - onRenameFileOperationFinish((RenameFileOperation) operation, result); - + onRenameFileOperationFinish((RenameFileOperation)operation, result); + } else if (operation instanceof SynchronizeFileOperation) { - onSynchronizeFileOperationFinish((SynchronizeFileOperation) operation, result); + onSynchronizeFileOperationFinish((SynchronizeFileOperation)operation, result); } } @@@ -1147,12 -1353,10 +1308,10 @@@ msg.show(); OCFile removedFile = operation.getFile(); if (mDualPane) { - FileDetailFragment details = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag( - FileDetailFragment.FTAG); - if (details != null && removedFile.equals(details.getDisplayedFile())) { + FileFragment details = (FileFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG); + if (details != null && removedFile.equals(details.getFile())) { FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null)); // empty - // FileDetailFragment + transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null)); // empty FileDetailFragment transaction.commit(); } } @@@ -1161,11 -1365,11 +1320,11 @@@ } } else { - Toast msg = Toast.makeText(this, R.string.remove_fail_msg, Toast.LENGTH_LONG); + Toast msg = Toast.makeText(this, R.string.remove_fail_msg, Toast.LENGTH_LONG); msg.show(); if (result.isSslRecoverableException()) { mLastSslUntrustedServerResult = result; - showDialog(DIALOG_SSL_VALIDATOR); + showDialog(DIALOG_SSL_VALIDATOR); } } } @@@ -1182,10 -1386,9 +1341,9 @@@ OCFile renamedFile = operation.getFile(); if (result.isSuccess()) { if (mDualPane) { - FileDetailFragment details = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag( - FileDetailFragment.FTAG); - if (details != null && renamedFile.equals(details.getDisplayedFile())) { - details.updateFileDetails(renamedFile, AccountUtils.getCurrentOwnCloudAccount(this)); + FileFragment details = (FileFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG); + if (details != null && details instanceof FileDetailFragment && renamedFile.equals(details.getFile()) ) { + ((FileDetailFragment) details).updateFileDetails(renamedFile, AccountUtils.getCurrentOwnCloudAccount(this)); } } if (mStorageManager.getFileById(renamedFile.getParentId()).equals(mCurrentDir)) { @@@ -1194,20 -1397,21 +1352,21 @@@ } else { if (result.getCode().equals(ResultCode.INVALID_LOCAL_FILE_NAME)) { - Toast msg = Toast.makeText(this, R.string.rename_local_fail_msg, Toast.LENGTH_LONG); + Toast msg = Toast.makeText(this, R.string.rename_local_fail_msg, Toast.LENGTH_LONG); msg.show(); // TODO throw again the new rename dialog } else { - Toast msg = Toast.makeText(this, R.string.rename_server_fail_msg, Toast.LENGTH_LONG); + Toast msg = Toast.makeText(this, R.string.rename_server_fail_msg, Toast.LENGTH_LONG); msg.show(); if (result.isSslRecoverableException()) { mLastSslUntrustedServerResult = result; - showDialog(DIALOG_SSL_VALIDATOR); + showDialog(DIALOG_SSL_VALIDATOR); } } } } + private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation, RemoteOperationResult result) { dismissDialog(DIALOG_SHORT_WAIT); OCFile syncedFile = operation.getLocalFile(); @@@ -1219,7 -1423,7 +1378,7 @@@ startActivity(i); } else { - Toast msg = Toast.makeText(this, R.string.sync_file_fail_msg, Toast.LENGTH_LONG); + Toast msg = Toast.makeText(this, R.string.sync_file_fail_msg, Toast.LENGTH_LONG); msg.show(); } @@@ -1229,7 -1433,7 +1388,7 @@@ onTransferStateChanged(syncedFile, true, true); } else { - Toast msg = Toast.makeText(this, R.string.sync_file_nothing_to_do_msg, Toast.LENGTH_LONG); + Toast msg = Toast.makeText(this, R.string.sync_file_nothing_to_do_msg, Toast.LENGTH_LONG); msg.show(); } } @@@ -1246,20 -1450,70 +1405,70 @@@ fileListFragment.listDirectory(); }*/ if (mDualPane) { - FileDetailFragment details = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag( - FileDetailFragment.FTAG); - if (details != null && file.equals(details.getDisplayedFile())) { + FileFragment details = (FileFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG); + if (details != null && details instanceof FileDetailFragment && file.equals(details.getFile()) ) { if (downloading || uploading) { - details.updateFileDetails(file, AccountUtils.getCurrentOwnCloudAccount(this)); + ((FileDetailFragment)details).updateFileDetails(file, AccountUtils.getCurrentOwnCloudAccount(this)); } else { - details.updateFileDetails(downloading || uploading); + ((FileDetailFragment)details).updateFileDetails(false, true); } } } } - + @Override + public void showFragmentWithDetails(OCFile file) { + if (mDualPane) { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.replace(R.id.file_details_container, new FileDetailFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG); + transaction.commit(); + + } else { + Intent showDetailsIntent = new Intent(this, FileDetailActivity.class); + showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file); + showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this)); + showDetailsIntent.putExtra(FileDetailActivity.EXTRA_MODE, FileDetailActivity.MODE_DETAILS); + startActivity(showDetailsIntent); + } + } + public void onDismiss(EditNameDialog dialog) { + //dialog.dismiss(); + if (dialog.getResult()) { + String newDirectoryName = dialog.getNewFilename().trim(); + Log.d(TAG, "'create directory' dialog dismissed with new name " + newDirectoryName); + if (newDirectoryName.length() > 0) { + String path; + if (mCurrentDir == null) { + // this is just a patch; we should ensure that mCurrentDir never is null + if (!mStorageManager.fileExists(OCFile.PATH_SEPARATOR)) { + OCFile file = new OCFile(OCFile.PATH_SEPARATOR); + mStorageManager.saveFile(file); + } + mCurrentDir = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR); + } + path = FileDisplayActivity.this.mCurrentDir.getRemotePath(); + + // Create directory + path += newDirectoryName + OCFile.PATH_SEPARATOR; + Thread thread = new Thread(new DirectoryCreator(path, AccountUtils.getCurrentOwnCloudAccount(FileDisplayActivity.this), new Handler())); + thread.start(); + + showDialog(DIALOG_SHORT_WAIT); + } + } + } + + private void requestForDownload() { + Account account = AccountUtils.getCurrentOwnCloudAccount(this); + if (!mDownloaderBinder.isDownloading(account, mWaitingToPreview)) { + Intent i = new Intent(this, FileDownloader.class); + i.putExtra(FileDownloader.EXTRA_ACCOUNT, account); + i.putExtra(FileDownloader.EXTRA_FILE, mWaitingToPreview); + startService(i); + } + } + } diff --combined 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 @@@ -32,6 -32,7 +32,7 @@@ import android.util.SparseArray import android.view.Gravity; import android.view.View; import android.view.View.OnClickListener; + import android.view.View.OnLongClickListener; import android.view.ViewGroup; import android.widget.Button; import android.widget.CheckBox; @@@ -39,23 -40,23 +40,24 @@@ import android.widget.CompoundButton import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.ImageButton; import android.widget.LinearLayout; + import android.widget.TextView; import android.widget.Toast; import com.owncloud.android.AccountUtils; +import com.owncloud.android.Log_OC; import com.owncloud.android.R; import com.owncloud.android.db.DbHandler; import com.owncloud.android.files.InstantUploadBroadcastReceiver; import com.owncloud.android.files.services.FileUploader; - import com.owncloud.android.files.services.InstantUploadService; + import com.owncloud.android.utils.FileStorageUtils; /** * This Activity is used to display a list with images they could not be * uploaded instantly. The images can be selected for delete or for a try again * upload * - * The entrypoint for this activity is the 'Failed upload Notification" and a - * submenue underneath the 'Upload' menuentry + * The entry-point for this activity is the 'Failed upload Notification" and a + * sub-menu underneath the 'Upload' menu-item * * @author andomaex / Matthias Baumann * @@@ -74,10 -75,12 +76,12 @@@ public class InstantUploadActivity exte private static final String LOG_TAG = InstantUploadActivity.class.getSimpleName(); private LinearLayout listView; private static final String retry_chexbox_tag = "retry_chexbox_tag"; + public static final boolean IS_ENABLED = false; private static int MAX_LOAD_IMAGES = 5; private int lastLoadImageIdx = 0; private SparseArray fileList = null; + CheckBox failed_upload_all_cb; @Override protected void onCreate(Bundle savedInstanceState) { @@@ -88,7 -91,7 +92,7 @@@ delete_all_btn.setOnClickListener(getDeleteListner()); Button retry_all_btn = (Button) findViewById(R.id.failed_upload_retry_all_btn); retry_all_btn.setOnClickListener(getRetryListner()); - CheckBox failed_upload_all_cb = (CheckBox) findViewById(R.id.failed_upload_headline_cb); + this.failed_upload_all_cb = (CheckBox) findViewById(R.id.failed_upload_headline_cb); failed_upload_all_cb.setOnCheckedChangeListener(getCheckAllListener()); listView = (LinearLayout) findViewById(R.id.failed_upload_scrollviewlayout); @@@ -122,13 -125,14 +126,14 @@@ 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; } @@@ -179,12 -183,12 +184,12 @@@ private List getCheckboxList() { List list = new ArrayList(); for (int i = 0; i < listView.getChildCount(); i++) { - Log.d(LOG_TAG, "ListView has Childs: " + listView.getChildCount()); + Log_OC.d(LOG_TAG, "ListView has Childs: " + listView.getChildCount()); View childView = listView.getChildAt(i); if (childView != null && childView instanceof ViewGroup) { View checkboxView = getChildViews((ViewGroup) childView); if (checkboxView != null && checkboxView instanceof CheckBox) { - Log.d(LOG_TAG, "found Child: " + checkboxView.getId() + " " + checkboxView.getClass()); + Log_OC.d(LOG_TAG, "found Child: " + checkboxView.getId() + " " + checkboxView.getClass()); list.add((CheckBox) checkboxView); } } @@@ -242,16 -246,17 +247,17 @@@ public void onClick(View v) { try { + List list = getCheckboxList(); for (CheckBox checkbox : list) { boolean to_retry = checkbox.isChecked(); - Log.d(LOG_TAG, "Checkbox for " + checkbox.getId() + " was checked: " + to_retry); + Log_OC.d(LOG_TAG, "Checkbox for " + checkbox.getId() + " was checked: " + to_retry); String img_path = fileList.get(checkbox.getId()); if (to_retry) { final String msg = "Image-Path " + checkbox.getId() + " was checked: " + img_path; - Log.d(LOG_TAG, msg); + Log_OC.d(LOG_TAG, msg); startUpload(img_path); } @@@ -260,6 -265,9 +266,9 @@@ // refresh the List listView.removeAllViews(); loadListView(true); + if (failed_upload_all_cb != null) { + failed_upload_all_cb.setChecked(false); + } } } @@@ -277,18 -285,19 +286,19 @@@ @Override public void onClick(View v) { + final DbHandler dbh = new DbHandler(getApplicationContext()); try { List list = getCheckboxList(); for (CheckBox checkbox : list) { boolean to_be_delete = checkbox.isChecked(); - Log.d(LOG_TAG, "Checkbox for " + checkbox.getId() + " was checked: " + to_be_delete); + Log_OC.d(LOG_TAG, "Checkbox for " + checkbox.getId() + " was checked: " + to_be_delete); String img_path = fileList.get(checkbox.getId()); - Log.d(LOG_TAG, "Image-Path " + checkbox.getId() + " was checked: " + img_path); + Log_OC.d(LOG_TAG, "Image-Path " + checkbox.getId() + " was checked: " + img_path); if (to_be_delete) { boolean deleted = dbh.removeIUPendingFile(img_path); - Log.d(LOG_TAG, "removing " + checkbox.getId() + " was : " + deleted); + Log_OC.d(LOG_TAG, "removing " + checkbox.getId() + " was : " + deleted); } @@@ -298,13 -307,16 +308,16 @@@ // refresh the List listView.removeAllViews(); loadListView(true); + if (failed_upload_all_cb != null) { + failed_upload_all_cb.setChecked(false); + } } } }; } - private LinearLayout getLinearLayout(int id) { + private LinearLayout getHorizontalLinearLayout(int id) { LinearLayout linearLayout = new LinearLayout(getApplicationContext()); linearLayout.setId(id); linearLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, @@@ -314,14 -326,51 +327,51 @@@ return linearLayout; } - private Button getFileButton(final String img_path, int id) { - Button retryButton = new Button(this); + private LinearLayout getVerticalLinearLayout() { + LinearLayout linearLayout = new LinearLayout(getApplicationContext()); + linearLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.MATCH_PARENT)); + linearLayout.setGravity(Gravity.TOP); + linearLayout.setOrientation(LinearLayout.VERTICAL); + return linearLayout; + } + + private View getFileButton(final String img_path, String message, int id) { + + TextView failureTextView = new TextView(this); + failureTextView.setText(getString(R.string.failed_upload_failure_text) + message); + failureTextView.setBackgroundResource(R.color.owncloud_white); + failureTextView.setTextSize(8); + failureTextView.setOnLongClickListener(getOnLongClickListener(message)); + failureTextView.setPadding(5, 5, 5, 10); + TextView retryButton = new TextView(this); retryButton.setId(id); retryButton.setText(img_path); retryButton.setBackgroundResource(R.color.owncloud_white); retryButton.setTextSize(8); retryButton.setOnClickListener(getImageButtonOnClickListener(img_path)); - return retryButton; + retryButton.setOnLongClickListener(getOnLongClickListener(message)); + retryButton.setPadding(5, 5, 5, 10); + LinearLayout verticalLayout = getVerticalLinearLayout(); + verticalLayout.addView(retryButton); + verticalLayout.addView(failureTextView); + + return verticalLayout; + } + + private OnLongClickListener getOnLongClickListener(final String message) { + return new OnLongClickListener() { + + @Override + public boolean onLongClick(View v) { + Log.d(LOG_TAG, message); + Toast toast = Toast.makeText(InstantUploadActivity.this, getString(R.string.failed_upload_retry_text) + + message, Toast.LENGTH_LONG); + toast.show(); + return true; + } + + }; } private CheckBox getFileCheckbox(int id) { @@@ -342,7 -391,7 +392,7 @@@ // scale and add a thumbnail to the imagebutton int base_scale_size = 32; if (img_path != null) { - Log.d(LOG_TAG, "add " + img_path + " to Image Button"); + Log_OC.d(LOG_TAG, "add " + img_path + " to Image Button"); BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; Bitmap bitmap = BitmapFactory.decodeFile(img_path, options); @@@ -357,16 -406,16 +407,16 @@@ scale++; } - Log.d(LOG_TAG, "scale Imgae with: " + scale); + Log_OC.d(LOG_TAG, "scale Imgae with: " + scale); BitmapFactory.Options options2 = new BitmapFactory.Options(); options2.inSampleSize = scale; bitmap = BitmapFactory.decodeFile(img_path, options2); if (bitmap != null) { - Log.d(LOG_TAG, "loaded Bitmap Bytes: " + bitmap.getRowBytes()); + Log_OC.d(LOG_TAG, "loaded Bitmap Bytes: " + bitmap.getRowBytes()); imageButton.setImageBitmap(bitmap); } else { - Log.d(LOG_TAG, "could not load imgage: " + img_path); + Log_OC.d(LOG_TAG, "could not load imgage: " + img_path); } } return imageButton; @@@ -391,13 -440,13 +441,13 @@@ */ private void startUpload(String img_path) { // extract filename - String filename = img_path.substring(img_path.lastIndexOf('/'), img_path.length()); + String filename = FileStorageUtils.getInstantUploadFilePath(img_path); if (canInstantUpload()) { Account account = AccountUtils.getCurrentOwnCloudAccount(InstantUploadActivity.this); // add file again to upload queue DbHandler db = new DbHandler(InstantUploadActivity.this); try { - db.updateFileState(img_path, DbHandler.UPLOAD_STATUS_UPLOAD_LATER); + db.updateFileState(img_path, DbHandler.UPLOAD_STATUS_UPLOAD_LATER, null); } finally { db.close(); } @@@ -405,12 -454,12 +455,12 @@@ Intent i = new Intent(InstantUploadActivity.this, FileUploader.class); i.putExtra(FileUploader.KEY_ACCOUNT, account); i.putExtra(FileUploader.KEY_LOCAL_FILE, img_path); - i.putExtra(FileUploader.KEY_REMOTE_FILE, InstantUploadService.INSTANT_UPLOAD_DIR + "/" + filename); + i.putExtra(FileUploader.KEY_REMOTE_FILE, filename); i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE); i.putExtra(com.owncloud.android.files.services.FileUploader.KEY_INSTANT_UPLOAD, true); final String msg = "try to upload file with name :" + filename; - Log.d(LOG_TAG, msg); + Log_OC.d(LOG_TAG, msg); Toast toast = Toast.makeText(InstantUploadActivity.this, getString(R.string.failed_upload_retry_text) + filename, Toast.LENGTH_LONG); toast.show(); diff --combined 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 @@@ -44,7 -44,6 +44,7 @@@ import com.owncloud.android.ui.fragment import com.owncloud.android.ui.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener; import com.owncloud.android.utils.FileStorageUtils; +import com.owncloud.android.Log_OC; import com.owncloud.android.R; /** @@@ -79,7 -78,7 +79,7 @@@ public class UploadFilesActivity extend @Override public void onCreate(Bundle savedInstanceState) { - Log.d(TAG, "onCreate() start"); + Log_OC.d(TAG, "onCreate() start"); super.onCreate(savedInstanceState); if(savedInstanceState != null) { @@@ -126,7 -125,7 +126,7 @@@ mCurrentDialog = null; } - Log.d(TAG, "onCreate() end"); + Log_OC.d(TAG, "onCreate() end"); } @@@ -182,10 -181,10 +182,10 @@@ @Override protected void onSaveInstanceState(Bundle outState) { // responsibility of restore is preferred in onCreate() before than in onRestoreInstanceState when there are Fragments involved - Log.d(TAG, "onSaveInstanceState() start"); + Log_OC.d(TAG, "onSaveInstanceState() start"); super.onSaveInstanceState(outState); outState.putString(UploadFilesActivity.KEY_DIRECTORY_PATH, mCurrentDir.getAbsolutePath()); - Log.d(TAG, "onSaveInstanceState() end"); + Log_OC.d(TAG, "onSaveInstanceState() end"); } @@@ -348,15 -347,14 +348,14 @@@ String[] args = {getString(R.string.app_name)}; ConfirmationDialogFragment dialog = ConfirmationDialogFragment.newInstance(R.string.upload_query_move_foreign_files, args, R.string.common_yes, -1, R.string.common_no); dialog.setOnConfirmationListener(UploadFilesActivity.this); - mCurrentDialog = dialog; - mCurrentDialog.show(getSupportFragmentManager(), QUERY_TO_MOVE_DIALOG_TAG); + dialog.show(getSupportFragmentManager(), QUERY_TO_MOVE_DIALOG_TAG); } } } @Override public void onConfirmation(String callerTag) { - Log.d(TAG, "Positive button in dialog was clicked; dialog tag is " + callerTag); + Log_OC.d(TAG, "Positive button in dialog was clicked; dialog tag is " + callerTag); if (callerTag.equals(QUERY_TO_MOVE_DIALOG_TAG)) { // return the list of selected files to the caller activity (success), signaling that they should be moved to the ownCloud folder, instead of copied Intent data = new Intent(); @@@ -364,25 -362,19 +363,19 @@@ setResult(RESULT_OK_AND_MOVE, data); finish(); } - mCurrentDialog.dismiss(); - mCurrentDialog = null; } @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 --combined src/com/owncloud/android/ui/fragment/ConfirmationDialogFragment.java index 679b2b9c,7a59bd65..7db3641a --- a/src/com/owncloud/android/ui/fragment/ConfirmationDialogFragment.java +++ b/src/com/owncloud/android/ui/fragment/ConfirmationDialogFragment.java @@@ -26,7 -26,6 +26,7 @@@ import android.os.Bundle import android.util.Log; import com.actionbarsherlock.app.SherlockDialogFragment; +import com.owncloud.android.Log_OC; public class ConfirmationDialogFragment extends SherlockDialogFragment { @@@ -37,6 -36,8 +37,8 @@@ public final static String ARG_NEUTRAL_BTN_RES = "neutral_btn_res"; public final static String ARG_NEGATIVE_BTN_RES = "negative_btn_res"; + public static final String FTAG_CONFIRMATION = "CONFIRMATION_FRAGMENT"; + private ConfirmationDialogFragmentListener mListener; /** @@@ -74,7 -75,7 +76,7 @@@ int negBtn = getArguments().getInt(ARG_NEGATIVE_BTN_RES, -1); if (confirmationTarget == null || resourceId == -1) { - Log.wtf(getTag(), "Calling confirmation dialog without resource or arguments"); + Log_OC.wtf(getTag(), "Calling confirmation dialog without resource or arguments"); return null; } @@@ -91,6 -92,7 +93,7 @@@ new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { mListener.onConfirmation(getTag()); + dialog.dismiss(); } }); if (neuBtn != -1) @@@ -98,6 -100,7 +101,7 @@@ new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { mListener.onNeutral(getTag()); + dialog.dismiss(); } }); if (negBtn != -1) @@@ -106,6 -109,7 +110,7 @@@ @Override public void onClick(DialogInterface dialog, int which) { mListener.onCancel(getTag()); + dialog.dismiss(); } }); return builder.create(); diff --combined 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 --combined 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 @@@ -23,7 -23,6 +23,7 @@@ import java.util.ArrayList import java.util.List; import com.owncloud.android.AccountUtils; +import com.owncloud.android.Log_OC; import com.owncloud.android.R; import com.owncloud.android.datamodel.DataStorageManager; import com.owncloud.android.datamodel.OCFile; @@@ -82,8 -81,6 +82,6 @@@ public class OCFileListFragment extend private Handler mHandler; private OCFile mTargetFile; - private DialogFragment mCurrentDialog; - /** * {@inheritDoc} */ @@@ -103,14 -100,14 +101,14 @@@ */ @Override public void onActivityCreated(Bundle savedInstanceState) { - Log.i(TAG, "onActivityCreated() start"); + Log_OC.i(TAG, "onActivityCreated() start"); super.onActivityCreated(savedInstanceState); mAdapter = new FileListListAdapter(mContainerActivity.getInitialDirectory(), mContainerActivity.getStorageManager(), getActivity(), mContainerActivity); setListAdapter(mAdapter); if (savedInstanceState != null) { - Log.i(TAG, "savedInstanceState is not null"); + Log_OC.i(TAG, "savedInstanceState is not null"); int position = savedInstanceState.getInt(SAVED_LIST_POSITION); setReferencePosition(position); } @@@ -120,18 -117,18 +118,18 @@@ mHandler = new Handler(); - Log.i(TAG, "onActivityCreated() stop"); + Log_OC.i(TAG, "onActivityCreated() stop"); } @Override public void onSaveInstanceState(Bundle savedInstanceState) { - Log.i(TAG, "onSaveInstanceState() start"); + Log_OC.i(TAG, "onSaveInstanceState() start"); savedInstanceState.putInt(SAVED_LIST_POSITION, getReferencePosition()); - Log.i(TAG, "onSaveInstanceState() stop"); + Log_OC.i(TAG, "onSaveInstanceState() stop"); } @@@ -152,7 -149,7 +150,7 @@@ } } else { - Log.d(TAG, "Null object in ListAdapter!!"); + Log_OC.d(TAG, "Null object in ListAdapter!!"); } } @@@ -164,7 -161,7 +162,7 @@@ public void onCreateContextMenu (ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); MenuInflater inflater = getActivity().getMenuInflater(); - inflater.inflate(R.menu.file_context_menu, menu); + inflater.inflate(R.menu.file_actions_menu, menu); AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; OCFile targetFile = (OCFile) mAdapter.getItem(info.position); List toHide = new ArrayList(); @@@ -173,46 -170,47 +171,47 @@@ MenuItem item = null; if (targetFile.isDirectory()) { // contextual menu for folders - toHide.add(R.id.open_file_item); - toHide.add(R.id.download_file_item); - toHide.add(R.id.cancel_download_item); - toHide.add(R.id.cancel_upload_item); + toHide.add(R.id.action_open_file_with); + toHide.add(R.id.action_download_file); + toHide.add(R.id.action_cancel_download); + toHide.add(R.id.action_cancel_upload); + toHide.add(R.id.action_see_details); if ( mContainerActivity.getFileDownloaderBinder().isDownloading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile) || mContainerActivity.getFileUploaderBinder().isUploading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile) ) { - toDisable.add(R.id.rename_file_item); - toDisable.add(R.id.remove_file_item); + toDisable.add(R.id.action_rename_file); + toDisable.add(R.id.action_remove_file); } } else { // contextual menu for regular files if (targetFile.isDown()) { - toHide.add(R.id.cancel_download_item); - toHide.add(R.id.cancel_upload_item); - item = menu.findItem(R.id.download_file_item); + toHide.add(R.id.action_cancel_download); + toHide.add(R.id.action_cancel_upload); + item = menu.findItem(R.id.action_download_file); if (item != null) { item.setTitle(R.string.filedetails_sync_file); } } else { - toHide.add(R.id.open_file_item); + toHide.add(R.id.action_open_file_with); } if ( mContainerActivity.getFileDownloaderBinder().isDownloading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile)) { - toHide.add(R.id.download_file_item); - toHide.add(R.id.cancel_upload_item); - toDisable.add(R.id.open_file_item); - toDisable.add(R.id.rename_file_item); - toDisable.add(R.id.remove_file_item); + toHide.add(R.id.action_download_file); + toHide.add(R.id.action_cancel_upload); + toDisable.add(R.id.action_open_file_with); + toDisable.add(R.id.action_rename_file); + toDisable.add(R.id.action_remove_file); } else if ( mContainerActivity.getFileUploaderBinder().isUploading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile)) { - toHide.add(R.id.download_file_item); - toHide.add(R.id.cancel_download_item); - toDisable.add(R.id.open_file_item); - toDisable.add(R.id.rename_file_item); - toDisable.add(R.id.remove_file_item); + toHide.add(R.id.action_download_file); + toHide.add(R.id.action_cancel_download); + toDisable.add(R.id.action_open_file_with); + toDisable.add(R.id.action_rename_file); + toDisable.add(R.id.action_remove_file); } else { - toHide.add(R.id.cancel_download_item); - toHide.add(R.id.cancel_upload_item); + toHide.add(R.id.action_cancel_download); + toHide.add(R.id.action_cancel_upload); } } @@@ -241,12 -239,12 +240,12 @@@ AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); mTargetFile = (OCFile) mAdapter.getItem(info.position); switch (item.getItemId()) { - case R.id.rename_file_item: { + case R.id.action_rename_file: { EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), mTargetFile.getFileName(), this); dialog.show(getFragmentManager(), EditNameDialog.TAG); return true; } - case R.id.remove_file_item: { + case R.id.action_remove_file: { int messageStringId = R.string.confirmation_remove_alert; int posBtnStringId = R.string.confirmation_remove_remote; int neuBtnStringId = -1; @@@ -265,11 -263,10 +264,10 @@@ neuBtnStringId, R.string.common_cancel); confDialog.setOnConfirmationListener(this); - mCurrentDialog = confDialog; - mCurrentDialog.show(getFragmentManager(), FileDetailFragment.FTAG_CONFIRMATION); + confDialog.show(getFragmentManager(), FileDetailFragment.FTAG_CONFIRMATION); return true; } - case R.id.open_file_item: { + case R.id.action_open_file_with: { String storagePath = mTargetFile.getStoragePath(); String encodedStoragePath = WebdavUtils.encodePath(storagePath); try { @@@ -279,7 -276,7 +277,7 @@@ startActivity(i); } catch (Throwable t) { - Log.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + mTargetFile.getMimetype()); + Log_OC.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + mTargetFile.getMimetype()); boolean toastIt = true; String mimeType = ""; try { @@@ -298,13 -295,13 +296,13 @@@ } } catch (IndexOutOfBoundsException e) { - Log.e(TAG, "Trying to find out MIME type of a file without extension: " + storagePath); + Log_OC.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"); + Log_OC.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); + Log_OC.e(TAG, "Unexpected problem when opening: " + storagePath, th); } finally { if (toastIt) { @@@ -315,7 -312,7 +313,7 @@@ } return true; } - case R.id.download_file_item: { + case R.id.action_download_file: { Account account = AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()); RemoteOperation operation = new SynchronizeFileOperation(mTargetFile, null, mContainerActivity.getStorageManager(), account, true, false, getSherlockActivity()); WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(account, getSherlockActivity().getApplicationContext()); @@@ -323,7 -320,7 +321,7 @@@ getSherlockActivity().showDialog(FileDisplayActivity.DIALOG_SHORT_WAIT); return true; } - case R.id.cancel_download_item: { + case R.id.action_cancel_download: { FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder(); Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity()); if (downloaderBinder != null && downloaderBinder.isDownloading(account, mTargetFile)) { @@@ -333,7 -330,7 +331,7 @@@ } return true; } - case R.id.cancel_upload_item: { + case R.id.action_cancel_upload: { FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder(); Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity()); if (uploaderBinder != null && uploaderBinder.isUploading(account, mTargetFile)) { @@@ -343,6 -340,10 +341,10 @@@ } return true; } + case R.id.action_see_details: { + ((FileFragment.ContainerActivity)getActivity()).showFragmentWithDetails(mTargetFile); + return true; + } default: return super.onContextItemSelected(item); } @@@ -403,7 -404,7 +405,7 @@@ // If that's not a directory -> List its parent if(!directory.isDirectory()){ - Log.w(TAG, "You see, that is not a directory -> " + directory.toString()); + Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString()); directory = storageManager.getFileById(directory.getParentId()); } @@@ -476,7 -477,7 +478,7 @@@ public void onDismiss(EditNameDialog dialog) { if (dialog.getResult()) { String newFilename = dialog.getNewFilename(); - Log.d(TAG, "name edit dialog dismissed with new name " + newFilename); + Log_OC.d(TAG, "name edit dialog dismissed with new name " + newFilename); RemoteOperation operation = new RenameFileOperation(mTargetFile, AccountUtils.getCurrentOwnCloudAccount(getActivity()), newFilename, @@@ -500,10 -501,6 +502,6 @@@ getActivity().showDialog(FileDisplayActivity.DIALOG_SHORT_WAIT); } - if (mCurrentDialog != null) { - mCurrentDialog.dismiss(); - mCurrentDialog = null; - } } } @@@ -519,21 -516,13 +517,13 @@@ mTargetFile.setStoragePath(null); mContainerActivity.getStorageManager().saveFile(mTargetFile); } - if (mCurrentDialog != null) { - mCurrentDialog.dismiss(); - mCurrentDialog = null; - } listDirectory(); mContainerActivity.onTransferStateChanged(mTargetFile, false, false); } @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 --combined 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,19 -30,19 +30,18 @@@ 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. * * @author David A. Velasco */ - public class ChunkFromFileChannelRequestEntity implements RequestEntity { + public class ChunkFromFileChannelRequestEntity implements RequestEntity, ProgressiveDataTransferer { private static final String TAG = ChunkFromFileChannelRequestEntity.class.getSimpleName(); @@@ -92,16 -92,25 +91,25 @@@ return true; } - public void addOnDatatransferProgressListener(OnDatatransferProgressListener listener) { - mDataTransferListeners.add(listener); + @Override + public void addDatatransferProgressListener(OnDatatransferProgressListener listener) { + synchronized (mDataTransferListeners) { + mDataTransferListeners.add(listener); + } } - public void addOnDatatransferProgressListeners(Collection listeners) { - mDataTransferListeners.addAll(listeners); + @Override + public void addDatatransferProgressListeners(Collection listeners) { + synchronized (mDataTransferListeners) { + mDataTransferListeners.addAll(listeners); + } } - public void removeOnDatatransferProgressListener(OnDatatransferProgressListener listener) { - mDataTransferListeners.remove(listener); + @Override + public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) { + synchronized (mDataTransferListeners) { + mDataTransferListeners.remove(listener); + } } @@@ -118,14 -127,16 +126,16 @@@ out.write(mBuffer.array(), 0, readCount); mBuffer.clear(); mTransferred += readCount; - it = mDataTransferListeners.iterator(); - while (it.hasNext()) { - it.next().onTransferProgress(readCount, mTransferred, size, mFile.getName()); + synchronized (mDataTransferListeners) { + it = mDataTransferListeners.iterator(); + while (it.hasNext()) { + it.next().onTransferProgress(readCount, mTransferred, size, mFile.getName()); + } } } } catch (IOException io) { - Log.e(TAG, io.getMessage()); + Log_OC.e(TAG, io.getMessage()); throw new RuntimeException("Ugly solution to workaround the default policy of retries when the server falls while uploading ; temporal fix; really", io); } diff --combined 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,18 -32,18 +32,17 @@@ 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. * */ - public class FileRequestEntity implements RequestEntity { + public class FileRequestEntity implements RequestEntity, ProgressiveDataTransferer { final File mFile; final String mContentType; @@@ -72,17 -72,26 +71,26 @@@ public boolean isRepeatable() { return true; } - - public void addOnDatatransferProgressListener(OnDatatransferProgressListener listener) { - mDataTransferListeners.add(listener); + + @Override + public void addDatatransferProgressListener(OnDatatransferProgressListener listener) { + synchronized (mDataTransferListeners) { + mDataTransferListeners.add(listener); + } } - public void addOnDatatransferProgressListeners(Collection listeners) { - mDataTransferListeners.addAll(listeners); + @Override + public void addDatatransferProgressListeners(Collection listeners) { + synchronized (mDataTransferListeners) { + mDataTransferListeners.addAll(listeners); + } } - public void removeOnDatatransferProgressListener(OnDatatransferProgressListener listener) { - mDataTransferListeners.remove(listener); + @Override + public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) { + synchronized (mDataTransferListeners) { + mDataTransferListeners.remove(listener); + } } @@@ -105,14 -114,16 +113,16 @@@ out.write(tmp.array(), 0, readResult); tmp.clear(); transferred += readResult; - it = mDataTransferListeners.iterator(); - while (it.hasNext()) { - it.next().onTransferProgress(readResult, transferred, size, mFile.getName()); + synchronized (mDataTransferListeners) { + it = mDataTransferListeners.iterator(); + while (it.hasNext()) { + it.next().onTransferProgress(readResult, transferred, size, mFile.getName()); + } } } } catch (IOException io) { - Log.e("FileRequestException", io.getMessage()); + Log_OC.e("FileRequestException", io.getMessage()); throw new RuntimeException("Ugly solution to workaround the default policy of retries when the server falls while uploading ; temporal fix; really", io); } finally { diff --combined 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; + } + +}