Browse Source

Merge branch 'master' into text_file_preview_pr_707_with_develop

Conflicts:
	src/com/owncloud/android/ui/activity/FileDisplayActivity.java
	src/com/owncloud/android/ui/fragment/OCFileListFragment.java
masensio 9 years ago
parent
commit
fc5c4d899c

+ 0 - 1
.gitmodules

@@ -1,4 +1,3 @@
-
 [submodule "owncloud-android-library"]
 	path = owncloud-android-library
 	url = git://github.com/owncloud/android-library.git

+ 26 - 38
AndroidManifest.xml

@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 
+<?xml version="1.0" encoding="utf-8"?><!--
   ownCloud Android client application
 
   Copyright (C) 2012  Bartek Przybylski
@@ -39,47 +38,38 @@
         android:minSdkVersion="14"
         android:targetSdkVersion="22" />
 
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" >
-    </uses-permission>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
 
     <application
         android:name=".MainApp"
         android:icon="@drawable/icon"
         android:label="@string/app_name"
-        android:theme="@style/Theme.ownCloud"> 
+        android:theme="@style/Theme.ownCloud">
         <activity
             android:name=".ui.activity.FileDisplayActivity"
-            android:label="@string/app_name"
-            >
+            android:label="@string/app_name">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
-        <activity android:name=".ui.activity.UploadFilesActivity">
-        </activity>
-        <activity android:name=".ui.activity.Uploader" >
+        <activity android:name=".ui.activity.UploadFilesActivity"></activity>
+        <activity android:name=".ui.activity.Uploader">
             <intent-filter>
-                <action android:name="android.intent.action.SEND" >
-                </action>
+                <action android:name="android.intent.action.SEND"></action>
 
-                <category android:name="android.intent.category.DEFAULT" >
-                </category>
+                <category android:name="android.intent.category.DEFAULT"></category>
 
-                <data android:mimeType="*/*" >
-                </data>
+                <data android:mimeType="*/*"></data>
             </intent-filter>
             <intent-filter>
-                <action android:name="android.intent.action.SEND_MULTIPLE" >
-                </action>
+                <action android:name="android.intent.action.SEND_MULTIPLE"></action>
 
-                <category android:name="android.intent.category.DEFAULT" >
-                </category>
+                <category android:name="android.intent.category.DEFAULT"></category>
 
-                <data android:mimeType="*/*" >
-                </data>
+                <data android:mimeType="*/*"></data>
 
-			</intent-filter>
+            </intent-filter>
         </activity>
         <activity
             android:name=".ui.activity.Preferences"
@@ -92,15 +82,13 @@
 		        
         <activity	
             android:name=".ui.preview.PreviewVideoActivity"
-			android:label="@string/app_name"
-			android:theme="@style/Theme.ownCloud.Fullscreen" 
-			>
-		</activity>        
+            android:label="@string/app_name"
+            android:theme="@style/Theme.ownCloud.Fullscreen"></activity>
 
         <service
             android:name=".authentication.AccountAuthenticatorService"
             android:exported="true">
-            <intent-filter  android:priority="100">
+            <intent-filter android:priority="100">
                 <action android:name="android.accounts.AccountAuthenticator" />
             </intent-filter>
 
@@ -110,8 +98,7 @@
         </service>
         <service
             android:name=".syncadapter.FileSyncService"
-            android:exported="true" 
-            >
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.content.SyncAdapter" />
             </intent-filter>
@@ -127,8 +114,7 @@
             android:enabled="true"
             android:exported="false"
             android:label="@string/sync_string_files"
-            android:syncable="true" >
-        </provider>
+            android:syncable="true"></provider>
 
         <activity
             android:name=".authentication.AuthenticatorActivity"
@@ -137,8 +123,10 @@
             android:launchMode="singleTask">
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
+
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.BROWSABLE" />
+
                 <data android:scheme="@string/oauth2_redirect_scheme" />
             </intent-filter>
             <intent-filter>
@@ -151,7 +139,7 @@
         <service android:name=".files.services.FileDownloader" />
         <service android:name=".files.services.FileUploader" />
         <service android:name=".media.MediaService" />
-        
+
         <activity android:name=".ui.activity.PassCodeActivity" />
         <activity android:name=".ui.activity.ConflictsResolveActivity"/>
         <activity android:name=".ui.activity.GenericExplanationActivity"/>
@@ -163,7 +151,7 @@
             <intent-filter>
                 <!-- unofficially supported by many Android phones but not by HTC devices: -->
                 <action android:name="com.android.camera.NEW_PICTURE" />
-                <!-- officially supported since Android 4.0 (SDK 14, works even for HTC devices): --> 
+                <!-- officially supported since Android 4.0 (SDK 14, works even for HTC devices): -->
                 <action android:name="android.hardware.action.NEW_PICTURE" />
                 <data android:mimeType="image/*" />
             </intent-filter>
@@ -172,12 +160,12 @@
                 <data android:mimeType="video/*" />
             </intent-filter>
             <intent-filter>
-                <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
+                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
             </intent-filter>
         </receiver>
         <receiver android:name=".files.BootupBroadcastReceiver">
             <intent-filter>
-                <action android:name="android.intent.action.BOOT_COMPLETED"/>
+                <action android:name="android.intent.action.BOOT_COMPLETED" />
             </intent-filter>
         </receiver>
         <service android:name=".services.observer.FileObserverService"/>
@@ -187,11 +175,11 @@
 			android:label="@string/copy_link"
 			android:icon="@drawable/copy_link"/>
 
-        <activity 
+        <activity
 			android:name=".ui.activity.FolderPickerActivity"
 			android:label="@string/app_name"/>
 
-        <activity 
+        <activity
 			android:name=".ui.activity.UploadPathActivity"
 			android:label="@string/app_name"/>
         

+ 0 - 6
build.gradle

@@ -58,12 +58,6 @@ android {
             abortOnError false
         }
     }
-
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
-    }
     
     productFlavors {
     }

+ 1 - 1
owncloud-android-library

@@ -1 +1 @@
-Subproject commit dbc8c325d74f3f7e8da8236c5abe77a141ae4019
+Subproject commit 17bb724b4207142d631a5f1c70d1e80c447e2310

+ 78 - 18
res/menu/file_actions_menu.xml

@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
   ownCloud Android client application
 
   Copyright (C) 2012  Bartek Przybylski
@@ -17,21 +16,82 @@
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 -->
-<menu 	xmlns:android="http://schemas.android.com/apk/res/android">
-    
-	<item 	android:id="@+id/action_share_file"				android:title="@string/action_share_file"			android:icon="@android:drawable/ic_menu_share"					android:orderInCategory="1" />
-   	<item 	android:id="@+id/action_unshare_file"		    android:title="@string/action_unshare_file"			android:icon="@android:drawable/ic_menu_share"					android:orderInCategory="1" />
-    <item 	android:id="@+id/action_open_file_with"			android:title="@string/actionbar_open_with"			android:icon="@android:drawable/ic_menu_set_as"					android:orderInCategory="1" /> 
-	<item 	android:id="@+id/action_download_file" 			android:title="@string/filedetails_download"		android:icon="@drawable/ic_action_download"						android:orderInCategory="1" />
-    <item 	android:id="@+id/action_sync_file"				android:title="@string/filedetails_sync_file"		android:icon="@drawable/ic_action_refresh"						android:orderInCategory="1" />
-	<item 	android:id="@+id/action_cancel_download"		android:title="@string/common_cancel_download"	 	android:icon="@android:drawable/ic_menu_close_clear_cancel"		android:orderInCategory="1" />
-	<item 	android:id="@+id/action_cancel_upload" 			android:title="@string/common_cancel_upload"		android:icon="@android:drawable/ic_menu_close_clear_cancel"		android:orderInCategory="1" />
-	<item 	android:id="@+id/action_rename_file"			android:title="@string/common_rename"				android:icon="@android:drawable/ic_menu_edit"					android:orderInCategory="1" />
-    <item 	android:id="@+id/action_move"					android:title="@string/actionbar_move"				android:icon="@android:drawable/ic_menu_set_as"					android:orderInCategory="1" />
-    <item 	android:id="@+id/action_remove_file"			android:title="@string/common_remove"				android:icon="@android:drawable/ic_menu_delete"					android:orderInCategory="1" />
-    <item 	android:id="@+id/action_send_file"				android:title="@string/actionbar_send_file"			android:icon="@android:drawable/ic_menu_set_as"					android:orderInCategory="1" />
-    <item 	android:id="@+id/action_favorite_file"			android:title="@string/favorite"		    	android:icon="@android:drawable/ic_menu_set_as"					android:orderInCategory="1" />
-    <item 	android:id="@+id/action_unfavorite_file"		android:title="@string/unfavorite"		    	    android:icon="@android:drawable/ic_menu_set_as"					android:orderInCategory="1" />
-    <item 	android:id="@+id/action_see_details"			android:title="@string/actionbar_see_details"		android:icon="@android:drawable/ic_menu_info_details"			android:orderInCategory="1" />
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item
+        android:id="@+id/action_share_file"
+        android:title="@string/action_share_file"
+        android:icon="@android:drawable/ic_menu_share"
+        android:orderInCategory="1" />
+    <item
+        android:id="@+id/action_unshare_file"
+        android:title="@string/action_unshare_file"
+        android:icon="@android:drawable/ic_menu_share"
+        android:orderInCategory="1" />
+    <item
+        android:id="@+id/action_open_file_with"
+        android:title="@string/actionbar_open_with"
+        android:icon="@android:drawable/ic_menu_set_as"
+        android:orderInCategory="1" />
+    <item
+        android:id="@+id/action_download_file"
+        android:title="@string/filedetails_download"
+        android:icon="@drawable/ic_action_download"
+        android:orderInCategory="1" />
+    <item
+        android:id="@+id/action_sync_file"
+        android:title="@string/filedetails_sync_file"
+        android:icon="@drawable/ic_action_refresh"
+        android:orderInCategory="1" />
+    <item
+        android:id="@+id/action_cancel_download"
+        android:title="@string/common_cancel_download"
+        android:icon="@android:drawable/ic_menu_close_clear_cancel"
+        android:orderInCategory="1" />
+    <item
+        android:id="@+id/action_cancel_upload"
+        android:title="@string/common_cancel_upload"
+        android:icon="@android:drawable/ic_menu_close_clear_cancel"
+        android:orderInCategory="1" />
+    <item
+        android:id="@+id/action_rename_file"
+        android:title="@string/common_rename"
+        android:icon="@android:drawable/ic_menu_edit"
+        android:orderInCategory="1" />
+    <item
+        android:id="@+id/action_move"
+        android:title="@string/actionbar_move"
+        android:icon="@android:drawable/ic_menu_set_as"
+        android:orderInCategory="1" />
+    <item
+        android:id="@+id/action_copy"
+        android:title="@android:string/copy"
+        android:icon="@android:drawable/ic_menu_set_as"
+        android:orderInCategory="1" />
+    <item
+        android:id="@+id/action_remove_file"
+        android:title="@string/common_remove"
+        android:icon="@android:drawable/ic_menu_delete"
+        android:orderInCategory="1" />
+    <item
+        android:id="@+id/action_send_file"
+        android:title="@string/actionbar_send_file"
+        android:icon="@android:drawable/ic_menu_set_as"
+        android:orderInCategory="1" />
+    <item
+        android:id="@+id/action_favorite_file"
+        android:title="@string/favorite"
+        android:icon="@android:drawable/ic_menu_set_as"
+        android:orderInCategory="1" />
+    <item
+        android:id="@+id/action_unfavorite_file"
+        android:title="@string/unfavorite"
+        android:icon="@android:drawable/ic_menu_set_as"
+        android:orderInCategory="1" />
+    <item
+        android:id="@+id/action_see_details"
+        android:title="@string/actionbar_see_details"
+        android:icon="@android:drawable/ic_menu_info_details"
+        android:orderInCategory="1" />
 
 </menu>

+ 16 - 0
res/values-pl/strings.xml

@@ -21,8 +21,12 @@
     	<item>Biggest - Smallest</item>-->
   <!--TODO re-enable when "Accounts" is available in Navigation Drawer-->
   <!--<string name="drawer_item_accounts">Accounts</string>-->
+  <string name="drawer_item_all_files">Wszystkie pliki</string>
   <!--TODO re-enable when "On Device" is available
     <string name="drawer_item_on_device">On device</string>-->
+  <string name="drawer_item_settings">Ustawienia</string>
+  <string name="drawer_item_logs">Logi</string>
+  <string name="drawer_close">Zamknij</string>
   <string name="drawer_open">Otwórz</string>
   <string name="prefs_category_general">Ogólne</string>
   <string name="prefs_category_more">Więcej</string>
@@ -66,6 +70,7 @@
   <string name="uploader_info_uploading">Wysyłanie</string>
   <string name="file_list_seconds_ago">sekund temu</string>
   <string name="file_list_empty">Pusto. Wyślij coś!</string>
+  <string name="file_list_loading">Wczytywanie...</string>
   <string name="local_file_list_empty">Nie ma plików w tym folderze.</string>
   <string name="filedetails_select_file">Dotknij plik aby wyświetlić dodatkowe informacje</string>
   <string name="filedetails_size">Rozmiar:</string>
@@ -197,6 +202,7 @@
   <string name="confirmation_remove_folder_alert">Czy naprawdę chcesz usunąć %1$s i jego zawartość?</string>
   <string name="confirmation_remove_local">Tylko lokalnie</string>
   <string name="confirmation_remove_folder_local">Tylko lokalnie</string>
+  <string name="confirmation_remove_remote">Z serwera</string>
   <string name="remove_success_msg">Usunięto</string>
   <string name="remove_fail_msg">Nie można usunąć</string>
   <string name="rename_dialog_title">Wprowadź nową nazwę</string>
@@ -247,7 +253,10 @@
   <string name="instant_upload_on_wifi">Wysyłaj zdjęcia tylko przez WiFi</string>
   <string name="instant_video_upload_on_wifi">Aktualizuj filmy tylko przez WiFi</string>
   <string name="instant_upload_path">/InstantUpload</string>
+  <string name="conflict_title">Konflikt pliku</string>
   <string name="conflict_keep_both">Zatrzymaj oba</string>
+  <string name="conflict_use_local_version">lokalna wersja</string>
+  <string name="conflict_use_server_version">wersja zdalna</string>
   <string name="preview_image_description">Podgląd</string>
   <string name="preview_image_error_unknown_format">Ten obrazek nie może zostać wyświetlony</string>
   <string name="error__upload__local_file_not_copied">%1$s nie może zostać skopiowany do lokalnego folderu %2$s</string>
@@ -299,7 +308,14 @@
   <string name="prefs_category_security">Bezpieczeństwo</string>
   <string name="prefs_instant_video_upload_path_title">Katalog wysyłania dla wideo</string>
   <string name="download_folder_failed_content">Pobieranie %1$s katalogu nie może zostać ukończone</string>
+  <string name="shared_subject_header">udostępniony</string>
+  <string name="with_you_subject_header">z tobą</string>
   <string name="auth_refresh_button">Odśwież połączenie</string>
   <string name="auth_host_address">Adres Serwera</string>
   <string name="common_error_out_memory">Brak wystarczającej pamięci</string>
+  <string name="username">Użytkownik</string>
+  <string name="file_list__footer__folder">1 folder</string>
+  <string name="file_list__footer__folders">%1$d folderów</string>
+  <string name="file_list__footer__file">1 plik</string>
+  <string name="file_list__footer__file_and_folder">1 plik , 1 folder</string>
 </resources>

+ 4 - 0
res/values-zh-rCN/strings.xml

@@ -202,6 +202,8 @@
   <string name="confirmation_remove_folder_alert">您确定要删除 %1$s 及其内容吗?</string>
   <string name="confirmation_remove_local">仅本地</string>
   <string name="confirmation_remove_folder_local">仅本地</string>
+  <string name="confirmation_remove_remote">来自服务器</string>
+  <string name="confirmation_remove_remote_and_local">远程 &amp; 本地</string>
   <string name="remove_success_msg">成功删除</string>
   <string name="remove_fail_msg">无法完成删除</string>
   <string name="rename_dialog_title">请输出新的名字</string>
@@ -256,6 +258,8 @@
   <string name="conflict_title">文件冲突</string>
   <string name="conflict_message">您想要保留哪个文件?如果您同时选中了两个版本,本地的文件的文件名将被加上一个数字</string>
   <string name="conflict_keep_both">保留两者</string>
+  <string name="conflict_use_local_version">本地版本</string>
+  <string name="conflict_use_server_version">服务器版本</string>
   <string name="preview_image_description">图片预览</string>
   <string name="preview_image_error_unknown_format">无法显示图片</string>
   <string name="error__upload__local_file_not_copied">无法复制 %1$s 到本地目录 %2$s</string>

+ 54 - 55
res/values/strings.xml

@@ -122,14 +122,14 @@
     <string name="sync_fail_ticker_unauthorized">Synchronization failed, you need to relogin</string>
     <string name="sync_fail_content">Synchronization of %1$s could not be completed</string>
     <string name="sync_fail_content_unauthorized">Invalid password for %1$s</string>
-	<string name="sync_conflicts_in_favourites_ticker">Conflicts found</string>
-	<string name="sync_conflicts_in_favourites_content">%1$d kept-in-sync files could not be sync\'ed</string>
+    <string name="sync_conflicts_in_favourites_ticker">Conflicts found</string>
+    <string name="sync_conflicts_in_favourites_content">%1$d kept-in-sync files could not be sync\'ed</string>
     <string name="sync_fail_in_favourites_ticker">Kept-in-sync files failed</string>
     <string name="sync_fail_in_favourites_content">Contents of %1$d files could not be sync\'ed (%2$d conflicts)</string>
     <string name="sync_foreign_files_forgotten_ticker">Some local files were forgotten</string>
     <string name="sync_foreign_files_forgotten_content">%1$d files out of the %2$s folder could not be copied into</string>
     <string name="sync_foreign_files_forgotten_explanation">As of version 1.3.16, files uploaded from this device are copied into the local %1$s folder to prevent data loss when a single file is synced with multiple accounts.\n\nDue to this change, all files uploaded in previous versions of this app were copied into the %2$s folder. However, an error prevented the completion of this operation during account synchronization. You may either leave the file(s) as is and remove the link to %3$s, or move the file(s) into the %1$s folder and retain the link to %4$s.\n\nListed below are the local file(s), and the remote file(s) in %5$s they were linked to.</string>
-	<string name="sync_current_folder_was_removed">Folder %1$s does not exist anymore</string>    
+    <string name="sync_current_folder_was_removed">Folder %1$s does not exist anymore</string>
     <string name="foreign_files_move">"Move all"</string>
     <string name="foreign_files_success">"All files were moved"</string>
     <string name="foreign_files_fail">"Some files could not be moved"</string>
@@ -152,20 +152,20 @@
     <string name="media_state_loading">"%1$s (loading)"</string>
     <string name="media_event_done">"%1$s playback finished"</string>
     <string name="media_err_nothing_to_play">No media file found</string>
-	<string name="media_err_no_account">No account provided</string>
-	<string name="media_err_not_in_owncloud">File not in a valid account</string>
-	<string name="media_err_unsupported">Unsupported media codec</string>
-	<string name="media_err_io">Media file could not be read</string>
-	<string name="media_err_malformed">Media file not correctly encoded</string>
-	<string name="media_err_timeout">Timed out while trying to play</string>
-	<string name="media_err_invalid_progressive_playback">Media file cannot be streamed</string>
-	<string name="media_err_unknown">Media file cannot be played with the stock media player</string>
-	<string name="media_err_security_ex">Security error trying to play %1$s</string>
-	<string name="media_err_io_ex">Input error trying to play %1$s</string>
-	<string name="media_err_unexpected">Unexpected error trying to play %1$s</string>
-	<string name="media_rewind_description">Rewind button</string>
-	<string name="media_play_pause_description">Play or pause button</string>
-	<string name="media_forward_description">Fast forward button</string>
+    <string name="media_err_no_account">No account provided</string>
+    <string name="media_err_not_in_owncloud">File not in a valid account</string>
+    <string name="media_err_unsupported">Unsupported media codec</string>
+    <string name="media_err_io">Media file could not be read</string>
+    <string name="media_err_malformed">Media file not correctly encoded</string>
+    <string name="media_err_timeout">Timed out while trying to play</string>
+    <string name="media_err_invalid_progressive_playback">Media file cannot be streamed</string>
+    <string name="media_err_unknown">Media file cannot be played with the stock media player</string>
+    <string name="media_err_security_ex">Security error trying to play %1$s</string>
+    <string name="media_err_io_ex">Input error trying to play %1$s</string>
+    <string name="media_err_unexpected">Unexpected error trying to play %1$s</string>
+    <string name="media_rewind_description">Rewind button</string>
+    <string name="media_play_pause_description">Play or pause button</string>
+    <string name="media_forward_description">Fast forward button</string>
 
 	<string name="auth_getting_authorization">Getting authorization&#8230;</string>
 	<string name="auth_trying_to_login">Trying to login&#8230;</string>
@@ -250,19 +250,19 @@
     <string name="ssl_validator_label_L">Location:</string>
     <string name="ssl_validator_label_validity">Validity:</string>
     <string name="ssl_validator_label_validity_from">From:</string>
-	<string name="ssl_validator_label_validity_to">To:</string>
-	<string name="ssl_validator_label_signature">Signature:</string>
-	<string name="ssl_validator_label_signature_algorithm">Algorithm:</string>
-	<string name="ssl_validator_null_cert">The certificate could not be shown.</string>
-	<string name="ssl_validator_no_info_about_error">- No information about the error</string>
-			
+    <string name="ssl_validator_label_validity_to">To:</string>
+    <string name="ssl_validator_label_signature">Signature:</string>
+    <string name="ssl_validator_label_signature_algorithm">Algorithm:</string>
+    <string name="ssl_validator_null_cert">The certificate could not be shown.</string>
+    <string name="ssl_validator_no_info_about_error">- No information about the error</string>
+
     <string name="placeholder_sentence">This is a placeholder</string>
     <string name="placeholder_filename">placeholder.txt</string>
     <string name="placeholder_filetype">PNG Image</string>
     <string name="placeholder_filesize">389 KB</string>
     <string name="placeholder_timestamp">2012/05/18 12:23 PM</string>
     <string name="placeholder_media_time">12:23:45</string>
-    
+
     <string name="instant_upload_on_wifi">Upload pictures via WiFi only</string>
     <string name="instant_video_upload_on_wifi">Upload videos via WiFi only</string>
     <string name="instant_upload_path">/InstantUpload</string>
@@ -274,7 +274,7 @@
     
     <string name="preview_image_description">Image preview</string>
     <string name="preview_image_error_unknown_format">This image cannot be shown</string>
-    
+
     <string name="error__upload__local_file_not_copied">%1$s could not be copied to %2$s local folder</string>
     <string name="prefs_instant_upload_path_title">Upload Path</string>
 
@@ -287,32 +287,24 @@
     <string name="share_link_password_title">Enter a password</string>
     <string name="share_link_empty_password">You must enter a password</string>
 
-	<string name="activity_chooser_send_file_title">Send</string>
+    <string name="network_error_socket_exception">An error occurred while connecting with the server.</string>
+    <string name="network_error_socket_timeout_exception">An error occurred while waiting for the server, the operation couldn\'t have been done</string>
+    <string name="network_error_connect_timeout_exception">An error occurred while waiting for the server, the operation couldn\'t have been done</string>
+    <string name="network_host_not_available">The operation couldn\'t be completed, server is unavailable</string>
+    <string name="empty" />
 
-	<string name="copy_link">Copy link</string>
-	<string name="clipboard_text_copied">Copied to clipboard</string>
+    <string name="forbidden_permissions">You do not have permission %s</string>
+    <string name="forbidden_permissions_rename">to rename this file</string>
+    <string name="forbidden_permissions_delete">to delete this file</string>
+    <string name="share_link_forbidden_permissions">to share this file</string>
+    <string name="unshare_link_forbidden_permissions">to unshare this file</string>
+    <string name="forbidden_permissions_create">to create the file</string>
+    <string name="uploader_upload_forbidden_permissions">to upload in this folder</string>
+    <string name="downloader_download_file_not_found">The file is no longer available on the server</string>
 
-	<string name="error_cant_bind_to_operations_service">Critical error: cannot perform operations</string>
-	
-	<string name="network_error_socket_exception">An error occurred while connecting with the server.</string>
-	<string name="network_error_socket_timeout_exception">An error occurred while waiting for the server, the operation couldn\'t have been done</string>
-	<string name="network_error_connect_timeout_exception">An error occurred while waiting for the server, the operation couldn\'t have been done</string>
-	<string name="network_host_not_available">The operation couldn\'t be completed, server is unavailable</string>
-
-	<string name="empty" />
-	
-	<string name="forbidden_permissions">You do not have permission %s</string>
-	<string name="forbidden_permissions_rename">to rename this file</string>
-	<string name="forbidden_permissions_delete">to delete this file</string>
-	<string name="share_link_forbidden_permissions">to share this file</string>
-	<string name="unshare_link_forbidden_permissions">to unshare this file</string>
-	<string name="forbidden_permissions_create">to create the file</string>
-	<string name="uploader_upload_forbidden_permissions">to upload in this folder</string>
-	<string name="downloader_download_file_not_found">The file is no longer available on the server</string>
-
-	<string name="prefs_category_accounts">Accounts</string>
-	<string name="prefs_add_account">Add account</string>
-	<string name="auth_redirect_non_secure_connection_title">Secure connection is redirected to an unsecured route.</string>
+    <string name="prefs_category_accounts">Accounts</string>
+    <string name="prefs_add_account">Add account</string>
+    <string name="auth_redirect_non_secure_connection_title">Secure connection is redirected through an unsecured route.</string>
 
 	<string name="actionbar_logger">Logs</string>
 	<string name="log_send_history_button">Send History</string>
@@ -326,13 +318,20 @@
 	<string name="file_list_empty_moving">Nothing in here. You can add a folder!</string>
 	<string name="folder_picker_choose_button_text">Choose</string>
 
-	<string name="move_file_not_found">Unable to move. Please check whether the file exists</string>
-	<string name="move_file_invalid_into_descendent">It is not possible to move a folder into a descendant</string>
-	<string name="move_file_invalid_overwrite">The file exists already in the destination folder</string>
-	<string name="move_file_error">An error occurred while trying to move this file or folder</string>
-	<string name="forbidden_permissions_move">to move this file</string>
+    <string name="move_file_not_found">Unable to move. Please check whether the file exists</string>
+    <string name="move_file_invalid_into_descendent">It is not possible to move a folder into a descendant</string>
+    <string name="move_file_invalid_overwrite">The file exists already in the destination folder</string>
+    <string name="move_file_error">An error occurred while trying to move this file or folder</string>
+    <string name="forbidden_permissions_move">to move this file</string>
+
+
+    <string name="copy_file_not_found">Unable to copy. Please check whether the file exists</string>
+    <string name="copy_file_invalid_into_descendent">It is not possible to copy a folder into a descendant</string>
+    <string name="copy_file_invalid_overwrite">The file exists already in the destination folder</string>
+    <string name="copy_file_error">An error occurred while trying to copy this file or folder</string>
+    <string name="forbidden_permissions_copy">to copy this file</string>
 
-	<string name="prefs_category_instant_uploading">Instant Uploads</string>
+    <string name="prefs_category_instant_uploading">Instant Uploads</string>
 	<string name="prefs_category_security">Security</string>
 
 	<string name="prefs_instant_video_upload_path_title">Upload Video Path</string>

+ 207 - 177
src/com/owncloud/android/datamodel/FileDataStorageManager.java

@@ -50,6 +50,12 @@ import com.owncloud.android.lib.resources.shares.OCShare;
 import com.owncloud.android.lib.resources.shares.ShareType;
 import com.owncloud.android.utils.FileStorageUtils;
 
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
 public class FileDataStorageManager {
 
     public static final int ROOT_PARENT_ID = 0;
@@ -60,7 +66,7 @@ public class FileDataStorageManager {
 
     private static String TAG = FileDataStorageManager.class.getSimpleName();
 
-    
+
     public FileDataStorageManager(Account account, ContentResolver cr) {
         mContentProviderClient = null;
         mContentResolver = cr;
@@ -73,7 +79,7 @@ public class FileDataStorageManager {
         mAccount = account;
     }
 
-    
+
     public void setAccount(Account account) {
         mAccount = account;
     }
@@ -97,7 +103,7 @@ public class FileDataStorageManager {
     public ContentProviderClient getContentProviderClient() {
         return mContentProviderClient;
     }
-    
+
 
     public OCFile getFileByPath(String path) {
         Cursor c = getCursorForValue(ProviderTableMeta.FILE_PATH, path);
@@ -141,7 +147,7 @@ public class FileDataStorageManager {
         return fileExists(ProviderTableMeta.FILE_PATH, path);
     }
 
-    
+
     public Vector<OCFile> getFolderContent(OCFile f/*, boolean onlyOnDevice*/) {
         if (f != null && f.isFolder() && f.getFileId() != -1) {
             // TODO Enable when "On Device" is recovered ?
@@ -151,8 +157,8 @@ public class FileDataStorageManager {
             return new Vector<OCFile>();
         }
     }
-    
-    
+
+
     public Vector<OCFile> getFolderImages(OCFile folder/*, boolean onlyOnDevice*/) {
         Vector<OCFile> ret = new Vector<OCFile>(); 
         if (folder != null) {
@@ -183,7 +189,7 @@ public class FileDataStorageManager {
         cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
         cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
         //if (file.getParentId() != DataStorageManager.ROOT_PARENT_ID)
-            cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
+        cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
         cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
         if (!file.isFolder())
             cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
@@ -200,8 +206,7 @@ public class FileDataStorageManager {
         cv.put(ProviderTableMeta.FILE_IS_DOWNLOADING, file.isDownloading());
         
         boolean sameRemotePath = fileExists(file.getRemotePath());
-        if (sameRemotePath ||
-                fileExists(file.getFileId())        ) {  // for renamed files
+        if (sameRemotePath ||                fileExists(file.getFileId())) {           // for renamed files; no more delete and create
 
             OCFile oldFile = null;
             if (sameRemotePath) {
@@ -215,12 +220,12 @@ public class FileDataStorageManager {
             if (getContentResolver() != null) {
                 getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv,
                         ProviderTableMeta._ID + "=?",
-                        new String[] { String.valueOf(file.getFileId()) });
+                        new String[]{String.valueOf(file.getFileId())});
             } else {
                 try {
                     getContentProviderClient().update(ProviderTableMeta.CONTENT_URI,
                             cv, ProviderTableMeta._ID + "=?",
-                            new String[] { String.valueOf(file.getFileId()) });
+                            new String[]{String.valueOf(file.getFileId())});
                 } catch (RemoteException e) {
                     Log_OC.e(TAG,
                             "Fail to insert insert file to database "
@@ -246,7 +251,7 @@ public class FileDataStorageManager {
                 long new_id = Long.parseLong(result_uri.getPathSegments()
                         .get(1));
                 file.setFileId(new_id);
-            }            
+            }
         }
 
 //        if (file.isFolder()) {
@@ -254,17 +259,17 @@ public class FileDataStorageManager {
 //        } else {
 //            updateFolderSize(file.getParentId());
 //        }
-        
+
         return overriden;
     }
 
 
     /**
      * Inserts or updates the list of files contained in a given folder.
-     * 
+     * <p/>
      * CALLER IS THE RESPONSIBLE FOR GRANTING RIGHT UPDATE OF INFORMATION, NOT THIS METHOD.
      * HERE ONLY DATA CONSISTENCY SHOULD BE GRANTED
-     *  
+     *
      * @param folder
      * @param updatedFiles
      * @param filesToRemove
@@ -314,9 +319,9 @@ public class FileDataStorageManager {
                 // updating an existing file
                 operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
                         withValues(cv).
-                        withSelection(  ProviderTableMeta._ID + "=?", 
-                                new String[] { String.valueOf(file.getFileId()) })
-                                .build());
+                        withSelection(ProviderTableMeta._ID + "=?",
+                                new String[]{String.valueOf(file.getFileId())})
+                        .build());
 
             } else {
                 // adding a new file
@@ -324,9 +329,9 @@ public class FileDataStorageManager {
                         withValues(cv).build());
             }
         }
-        
+
         // prepare operations to remove files in the given folder
-        String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?" + " AND " + 
+        String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?" + " AND " +
                 ProviderTableMeta.FILE_PATH + "=?";
         String [] whereArgs = null;
         for (OCFile file : filesToRemove) {
@@ -351,7 +356,7 @@ public class FileDataStorageManager {
                                     ProviderTableMeta.CONTENT_URI_FILE, file.getFileId()
                             )
                     ).withSelection(where, whereArgs).build());
-                    
+
                     if (file.isDown()) {
                         String path = file.getStoragePath();
                         new File(path).delete();
@@ -360,7 +365,7 @@ public class FileDataStorageManager {
                 }
             }
         }
-        
+
         // update metadata of folder
         ContentValues cv = new ContentValues();
         cv.put(ProviderTableMeta.FILE_MODIFIED, folder.getModificationTimestamp());
@@ -383,12 +388,12 @@ public class FileDataStorageManager {
         cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, folder.getPublicLink());
         cv.put(ProviderTableMeta.FILE_PERMISSIONS, folder.getPermissions());
         cv.put(ProviderTableMeta.FILE_REMOTE_ID, folder.getRemoteId());
-        
+
         operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
                 withValues(cv).
-                withSelection(  ProviderTableMeta._ID + "=?", 
-                        new String[] { String.valueOf(folder.getFileId()) })
-                        .build());
+                withSelection(ProviderTableMeta._ID + "=?",
+                        new String[]{String.valueOf(folder.getFileId())})
+                .build());
 
         // apply operations in batch
         ContentProviderResult[] results = null;
@@ -413,7 +418,7 @@ public class FileDataStorageManager {
             long newId;
             Iterator<OCFile> filesIt = updatedFiles.iterator();
             OCFile file = null;
-            for (int i=0; i<results.length; i++) {
+            for (int i = 0; i < results.length; i++) {
                 if (filesIt.hasNext()) {
                     file = filesIt.next();
                 } else {
@@ -428,9 +433,9 @@ public class FileDataStorageManager {
                 }
             }
         }
-        
+
         //updateFolderSize(folder.getFileId());
-        
+
     }
 
 
@@ -464,23 +469,20 @@ public class FileDataStorageManager {
 //            Log_OC.e(TAG,  "not updating size for folder " + id);
 //        }
 //    }
-    
+
 
     public boolean removeFile(OCFile file, boolean removeDBData, boolean removeLocalCopy) {
         boolean success = true;
         if (file != null) {
             if (file.isFolder()) {
                 success = removeFolder(file, removeDBData, removeLocalCopy);
-                
+
             } else {
                 if (removeDBData) {
-                    Uri file_uri = ContentUris.withAppendedId(
-                        ProviderTableMeta.CONTENT_URI_FILE, 
-                        file.getFileId()
-                    );
-                    String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?" + " AND " + 
-                            ProviderTableMeta.FILE_PATH + "=?";
-                    String [] whereArgs = new String[]{mAccount.name, file.getRemotePath()};
+                    //Uri file_uri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, ""+file.getFileId());
+                    Uri file_uri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, file.getFileId());
+                    String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?" + " AND " + ProviderTableMeta.FILE_PATH + "=?";
+                    String[] whereArgs = new String[]{mAccount.name, file.getRemotePath()};
                     int deleted = 0;
                     if (getContentProviderClient() != null) {
                         try {
@@ -491,7 +493,7 @@ public class FileDataStorageManager {
                     } else {
                         deleted = getContentResolver().delete(file_uri, where, whereArgs);
                     }
-                    success &= (deleted > 0); 
+                    success &= (deleted > 0);
                 }
                 String localPath = file.getStoragePath();
                 if (removeLocalCopy && file.isDown() && localPath != null && success) {
@@ -509,12 +511,12 @@ public class FileDataStorageManager {
         }
         return success;
     }
-    
+
 
     public boolean removeFolder(OCFile folder, boolean removeDBData, boolean removeLocalContent) {
         boolean success = true;
         if (folder != null && folder.isFolder()) {
-            if (removeDBData &&  folder.getFileId() != -1) {
+            if (removeDBData && folder.getFileId() != -1) {
                 success = removeFolderInDb(folder);
             }
             if (removeLocalContent && success) {
@@ -525,7 +527,7 @@ public class FileDataStorageManager {
     }
 
     private boolean removeFolderInDb(OCFile folder) {
-        Uri folder_uri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_DIR, "" + 
+        Uri folder_uri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_DIR, "" +
                 folder.getFileId());   // URI for recursive deletion
         String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?" + " AND " + 
                 ProviderTableMeta.FILE_PATH + "=?";
@@ -538,7 +540,7 @@ public class FileDataStorageManager {
                 e.printStackTrace();
             }
         } else {
-            deleted = getContentResolver().delete(folder_uri, where, whereArgs); 
+            deleted = getContentResolver().delete(folder_uri, where, whereArgs);
         }
         return deleted > 0;
     }
@@ -597,54 +599,54 @@ public class FileDataStorageManager {
     
     /**
      * Updates database and file system for a file or folder that was moved to a different location.
-     * 
+     *
      * TODO explore better (faster) implementations
      * TODO throw exceptions up !
      */
     public void moveLocalFile(OCFile file, String targetPath, String targetParentPath) {
 
         if (file != null && file.fileExists() && !OCFile.ROOT_PATH.equals(file.getFileName())) {
-            
+
             OCFile targetParent = getFileByPath(targetParentPath);
             if (targetParent == null) {
                 throw new IllegalStateException("Parent folder of the target path does not exist!!");
             }
-            
+
             /// 1. get all the descendants of the moved element in a single QUERY
             Cursor c = null;
             if (getContentProviderClient() != null) {
                 try {
                     c = getContentProviderClient().query(
-                        ProviderTableMeta.CONTENT_URI, 
-                        null,
-                        ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + 
-                                ProviderTableMeta.FILE_PATH + " LIKE ? ",
-                        new String[] { 
-                                mAccount.name, 
-                                file.getRemotePath() + "%"  
-                        }, 
-                        ProviderTableMeta.FILE_PATH + " ASC "
+                            ProviderTableMeta.CONTENT_URI,
+                            null,
+                            ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " +
+                                    ProviderTableMeta.FILE_PATH + " LIKE ? ",
+                            new String[]{
+                                    mAccount.name,
+                                    file.getRemotePath() + "%"
+                            },
+                            ProviderTableMeta.FILE_PATH + " ASC "
                     );
                 } catch (RemoteException e) {
                     Log_OC.e(TAG, e.getMessage());
                 }
-                
+
             } else {
                 c = getContentResolver().query(
-                    ProviderTableMeta.CONTENT_URI, 
-                    null,
-                    ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + 
-                            ProviderTableMeta.FILE_PATH + " LIKE ? ",
-                    new String[] { 
-                            mAccount.name, 
-                            file.getRemotePath() + "%"  
-                    }, 
-                    ProviderTableMeta.FILE_PATH + " ASC "
+                        ProviderTableMeta.CONTENT_URI,
+                        null,
+                        ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " +
+                                ProviderTableMeta.FILE_PATH + " LIKE ? ",
+                        new String[]{
+                                mAccount.name,
+                                file.getRemotePath() + "%"
+                        },
+                        ProviderTableMeta.FILE_PATH + " ASC "
                 );
             }
 
             /// 2. prepare a batch of update operations to change all the descendants
-            ArrayList<ContentProviderOperation> operations = 
+            ArrayList<ContentProviderOperation> operations =
                     new ArrayList<ContentProviderOperation>(c.getCount());
             String defaultSavePath = FileStorageUtils.getSavePath(mAccount.name);
             List<String> originalPathsToTriggerMediaScan = new ArrayList<String>();
@@ -656,13 +658,13 @@ public class FileDataStorageManager {
                     ContentValues cv = new ContentValues(); // keep construction in the loop
                     OCFile child = createFileInstance(c);
                     cv.put(
-                        ProviderTableMeta.FILE_PATH, 
-                        targetPath + child.getRemotePath().substring(lengthOfOldPath)
+                            ProviderTableMeta.FILE_PATH,
+                            targetPath + child.getRemotePath().substring(lengthOfOldPath)
                     );
-                    if (child.getStoragePath() != null && 
+                    if (child.getStoragePath() != null &&
                             child.getStoragePath().startsWith(defaultSavePath)) {
                         // update link to downloaded content - but local move is not done here!
-                        String targetLocalPath = defaultSavePath + targetPath + 
+                        String targetLocalPath = defaultSavePath + targetPath +
                                 child.getStoragePath().substring(lengthOfOldStoragePath);
                         
                         cv.put(ProviderTableMeta.FILE_STORAGE_PATH, targetLocalPath);
@@ -675,17 +677,17 @@ public class FileDataStorageManager {
                         cv.put(
                                 ProviderTableMeta.FILE_PARENT,
                                 targetParent.getFileId()
-                            );
+                        );
                     }
                     operations.add(
-                        ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
-                            withValues(cv).
-                            withSelection(  
-                                    ProviderTableMeta._ID + "=?", 
-                                    new String[] { String.valueOf(child.getFileId()) }
+                            ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
+                                    withValues(cv).
+                                    withSelection(
+                                            ProviderTableMeta._ID + "=?",
+                                            new String[]{String.valueOf(child.getFileId())}
                                     )
-                            .build());
-                    
+                                    .build());
+
                 } while (c.moveToNext());
             }
             c.close();
@@ -730,9 +732,60 @@ public class FileDataStorageManager {
                 }
             }
         }
-        
+
     }
-    
+
+    public void copyLocalFile(OCFile file, String targetPath) {
+
+        if (file != null && file.fileExists() && !OCFile.ROOT_PATH.equals(file.getFileName())) {
+            String localPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, file);
+            File localFile = new File(localPath);
+            boolean copied = false;
+            String defaultSavePath = FileStorageUtils.getSavePath(mAccount.name);
+            if (localFile.exists()) {
+                File targetFile = new File(defaultSavePath + targetPath);
+                File targetFolder = targetFile.getParentFile();
+                if (!targetFolder.exists()) {
+                    targetFolder.mkdirs();
+                }
+                copied = copyFile(localFile, targetFile);
+            }
+            Log_OC.d(TAG, "Local file COPIED : " + copied);
+        }
+    }
+
+    private boolean copyFile(File src, File target) {
+        boolean ret = true;
+
+        InputStream in = null;
+        OutputStream out = null;
+
+        try {
+            in = new FileInputStream(src);
+            out = new FileOutputStream(target);
+            byte[] buf = new byte[1024];
+            int len;
+            while ((len = in.read(buf)) > 0) {
+                out.write(buf, 0, len);
+            }
+        } catch (IOException ex) {
+            ret = false;
+        } finally {
+            if (in != null) try {
+                in.close();
+            } catch (IOException e) {
+                e.printStackTrace(System.err);
+            }
+            if (out != null) try {
+                out.close();
+            } catch (IOException e) {
+                e.printStackTrace(System.err);
+            }
+        }
+
+        return ret;
+    }
+
     
     private Vector<OCFile> getFolderContent(long parentId/*, boolean onlyOnDevice*/) {
 
@@ -745,17 +798,17 @@ public class FileDataStorageManager {
 
         if (getContentProviderClient() != null) {
             try {
-                c = getContentProviderClient().query(req_uri, null, 
-                        ProviderTableMeta.FILE_PARENT + "=?" ,
-                        new String[] { String.valueOf(parentId)}, null);
+                c = getContentProviderClient().query(req_uri, null,
+                        ProviderTableMeta.FILE_PARENT + "=?",
+                        new String[]{String.valueOf(parentId)}, null);
             } catch (RemoteException e) {
                 Log_OC.e(TAG, e.getMessage());
                 return ret;
             }
         } else {
-            c = getContentResolver().query(req_uri, null, 
-                    ProviderTableMeta.FILE_PARENT + "=?" ,
-                    new String[] { String.valueOf(parentId)}, null);
+            c = getContentResolver().query(req_uri, null,
+                    ProviderTableMeta.FILE_PARENT + "=?",
+                    new String[]{String.valueOf(parentId)}, null);
         }
 
         if (c.moveToFirst()) {
@@ -774,8 +827,8 @@ public class FileDataStorageManager {
 
         return ret;
     }
-    
-    
+
+
     private OCFile createRootDir() {
         OCFile file = new OCFile(OCFile.ROOT_PATH);
         file.setMimetype("DIR");
@@ -793,7 +846,7 @@ public class FileDataStorageManager {
                             cmp_key + "=? AND "
                                     + ProviderTableMeta.FILE_ACCOUNT_OWNER
                                     + "=?",
-                                    new String[] { value, mAccount.name }, null);
+                            new String[]{value, mAccount.name}, null);
         } else {
             try {
                 c = getContentProviderClient().query(
@@ -801,7 +854,7 @@ public class FileDataStorageManager {
                         null,
                         cmp_key + "=? AND "
                                 + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?",
-                                new String[] { value, mAccount.name }, null);
+                        new String[]{value, mAccount.name}, null);
             } catch (RemoteException e) {
                 Log_OC.e(TAG,
                         "Couldn't determine file existance, assuming non existance: "
@@ -823,14 +876,14 @@ public class FileDataStorageManager {
                             key + "=? AND "
                                     + ProviderTableMeta.FILE_ACCOUNT_OWNER
                                     + "=?",
-                                    new String[] { value, mAccount.name }, null);
+                            new String[]{value, mAccount.name}, null);
         } else {
             try {
                 c = getContentProviderClient().query(
                         ProviderTableMeta.CONTENT_URI,
                         null,
                         key + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER
-                        + "=?", new String[] { value, mAccount.name },
+                                + "=?", new String[]{value, mAccount.name},
                         null);
             } catch (RemoteException e) {
                 Log_OC.e(TAG, "Could not get file details: " + e.getMessage());
@@ -839,7 +892,7 @@ public class FileDataStorageManager {
         }
         return c;
     }
-    
+
 
     private OCFile createFileInstance(Cursor c) {
         OCFile file = null;
@@ -893,10 +946,11 @@ public class FileDataStorageManager {
         }
         return file;
     }
-    
+
     /**
      * Returns if the file/folder is shared by link or not
-     * @param path  Path of the file/folder
+     *
+     * @param path Path of the file/folder
      * @return
      */
     public boolean isShareByLink(String path) {
@@ -908,10 +962,11 @@ public class FileDataStorageManager {
         c.close();
         return file.isShareByLink();
     }
-    
+
     /**
      * Returns the public link of the file/folder
-     * @param path  Path of the file/folder
+     *
+     * @param path Path of the file/folder
      * @return
      */
     public String getPublicLink(String path) {
@@ -923,8 +978,8 @@ public class FileDataStorageManager {
         c.close();
         return file.getPublicLink();
     }
-    
-    
+
+
     // Methods for Shares
     public boolean saveShare(OCShare share) {
         boolean overriden = false;
@@ -946,19 +1001,18 @@ public class FileDataStorageManager {
         cv.put(ProviderTableMeta.OCSHARES_USER_ID, share.getUserId());
         cv.put(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED, share.getIdRemoteShared());
         cv.put(ProviderTableMeta.OCSHARES_ACCOUNT_OWNER, mAccount.name);
-        
-        if (shareExists(share.getIdRemoteShared())) {   // for renamed files
 
+        if (shareExists(share.getIdRemoteShared())) {           // for renamed files; no more delete and create
             overriden = true;
             if (getContentResolver() != null) {
                 getContentResolver().update(ProviderTableMeta.CONTENT_URI_SHARE, cv,
                         ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + "=?",
-                        new String[] { String.valueOf(share.getIdRemoteShared()) });
+                        new String[]{String.valueOf(share.getIdRemoteShared())});
             } else {
                 try {
                     getContentProviderClient().update(ProviderTableMeta.CONTENT_URI_SHARE,
                             cv, ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + "=?",
-                            new String[] { String.valueOf(share.getIdRemoteShared()) });
+                            new String[]{String.valueOf(share.getIdRemoteShared())});
                 } catch (RemoteException e) {
                     Log_OC.e(TAG,
                             "Fail to insert insert file to database "
@@ -984,7 +1038,7 @@ public class FileDataStorageManager {
                 long new_id = Long.parseLong(result_uri.getPathSegments()
                         .get(1));
                 share.setId(new_id);
-            }            
+            }
         }
 
         return overriden;
@@ -1000,7 +1054,7 @@ public class FileDataStorageManager {
                     ProviderTableMeta.OCSHARES_PATH + "=? AND "
                             + ProviderTableMeta.OCSHARES_SHARE_TYPE + "=? AND "
                             + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?",
-                    new String[] { path, Integer.toString(type.getValue()), mAccount.name },
+                    new String[]{path, Integer.toString(type.getValue()), mAccount.name},
                     null);
         } else {
             try {
@@ -1010,7 +1064,7 @@ public class FileDataStorageManager {
                         ProviderTableMeta.OCSHARES_PATH + "=? AND "
                                 + ProviderTableMeta.OCSHARES_SHARE_TYPE + "=? AND "
                                 + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?",
-                        new String[] { path, Integer.toString(type.getValue()), mAccount.name }, 
+                        new String[]{path, Integer.toString(type.getValue()), mAccount.name},
                         null);
 
             } catch (RemoteException e) {
@@ -1025,7 +1079,7 @@ public class FileDataStorageManager {
         c.close();
         return share;
     }
-    
+
     private OCShare createShareInstance(Cursor c) {
         OCShare share = null;
         if (c != null) {
@@ -1047,12 +1101,9 @@ public class FileDataStorageManager {
             share.setSharedWithDisplayName(c.getString(c
                     .getColumnIndex(ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME)));
             share.setIsFolder(c.getInt(
-                    c.getColumnIndex(ProviderTableMeta.OCSHARES_IS_DIRECTORY)) == 1 ? true : false);
+                    c.getColumnIndex(ProviderTableMeta.OCSHARES_IS_DIRECTORY)) == 1);
             share.setUserId(c.getLong(c.getColumnIndex(ProviderTableMeta.OCSHARES_USER_ID)));
-            share.setIdRemoteShared(
-                c.getLong(c.getColumnIndex(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED))
-            );
-                    
+            share.setIdRemoteShared(c.getLong(c.getColumnIndex(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED)));
         }
         return share;
     }
@@ -1066,7 +1117,7 @@ public class FileDataStorageManager {
                             cmp_key + "=? AND "
                                     + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER
                                     + "=?",
-                                    new String[] { value, mAccount.name }, null);
+                            new String[]{value, mAccount.name}, null);
         } else {
             try {
                 c = getContentProviderClient().query(
@@ -1074,7 +1125,7 @@ public class FileDataStorageManager {
                         null,
                         cmp_key + "=? AND "
                                 + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?",
-                                new String[] { value, mAccount.name }, null);
+                        new String[]{value, mAccount.name}, null);
             } catch (RemoteException e) {
                 Log_OC.e(TAG,
                         "Couldn't determine file existance, assuming non existance: "
@@ -1086,7 +1137,7 @@ public class FileDataStorageManager {
         c.close();
         return retval;
     }
-    
+
     private boolean shareExists(long remoteId) {
         return shareExists(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED, String.valueOf(remoteId));
     }
@@ -1096,17 +1147,14 @@ public class FileDataStorageManager {
         cv.put(ProviderTableMeta.FILE_SHARE_BY_LINK, false);
         cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, "");
         String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?";
-        String [] whereArgs = new String[]{mAccount.name};
-        
+        String[] whereArgs = new String[]{mAccount.name};
+
         if (getContentResolver() != null) {
             getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv, where, whereArgs);
 
         } else {
             try {
-                getContentProviderClient().update(
-                        ProviderTableMeta.CONTENT_URI, cv, where, whereArgs
-                );
-                
+                getContentProviderClient().update(ProviderTableMeta.CONTENT_URI, cv, where, whereArgs);
             } catch (RemoteException e) {
                 Log_OC.e(TAG, "Exception in cleanSharedFiles" + e.getMessage());
             }
@@ -1117,7 +1165,7 @@ public class FileDataStorageManager {
         ContentValues cv = new ContentValues();
         cv.put(ProviderTableMeta.FILE_SHARE_BY_LINK, false);
         cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, "");
-        String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + 
+        String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " +
                 ProviderTableMeta.FILE_PARENT + "=?";
         String [] whereArgs = new String[] { mAccount.name , String.valueOf(folder.getFileId()) };
         
@@ -1126,10 +1174,7 @@ public class FileDataStorageManager {
 
         } else {
             try {
-                getContentProviderClient().update(
-                        ProviderTableMeta.CONTENT_URI, cv, where, whereArgs
-                );
-                
+                getContentProviderClient().update(ProviderTableMeta.CONTENT_URI, cv, where, whereArgs);
             } catch (RemoteException e) {
                 Log_OC.e(TAG, "Exception in cleanSharedFilesInFolder " + e.getMessage());
             }
@@ -1138,23 +1183,20 @@ public class FileDataStorageManager {
 
     private void cleanShares() {
         String where = ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?";
-        String [] whereArgs = new String[]{mAccount.name};
-        
+        String[] whereArgs = new String[]{mAccount.name};
+
         if (getContentResolver() != null) {
             getContentResolver().delete(ProviderTableMeta.CONTENT_URI_SHARE, where, whereArgs);
 
         } else {
             try {
-                getContentProviderClient().delete(
-                        ProviderTableMeta.CONTENT_URI_SHARE, where, whereArgs
-                );
-                
+                getContentProviderClient().delete(ProviderTableMeta.CONTENT_URI_SHARE, where, whereArgs);
             } catch (RemoteException e) {
                 Log_OC.e(TAG, "Exception in cleanShares" + e.getMessage());
             }
         }
     }
-    
+
     public void saveShares(Collection<OCShare> shares) {
         cleanShares();
         if (shares != null) {
@@ -1187,13 +1229,9 @@ public class FileDataStorageManager {
                     operations.add(
                             ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI_SHARE).
                             withValues(cv).
-                            withSelection(
-                                    ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + "=?", 
-                                    new String[] { String.valueOf(share.getIdRemoteShared()) }
-                            ).
-                            build()
-                    );
-
+                            withSelection(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + "=?",
+                                    new String[]{String.valueOf(share.getIdRemoteShared())})
+                            .build());
                 } else {
                     // adding a new file
                     operations.add(
@@ -1203,7 +1241,7 @@ public class FileDataStorageManager {
                     );
                 }
             }
-            
+
             // apply operations in batch
             if (operations.size() > 0) {
                 @SuppressWarnings("unused")
@@ -1212,28 +1250,25 @@ public class FileDataStorageManager {
                         " operations to FileContentProvider");
                 try {
                     if (getContentResolver() != null) {
-                        results = getContentResolver().applyBatch(
-                                MainApp.getAuthority(), operations
-                        );
-    
+                        results = getContentResolver().applyBatch(MainApp.getAuthority(), operations);
                     } else {
                         results = getContentProviderClient().applyBatch(operations);
                     }
-    
+
                 } catch (OperationApplicationException e) {
                     Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
-    
+
                 } catch (RemoteException e) {
                     Log_OC.e(TAG, "Exception in batch of operations  " + e.getMessage());
                 }
             }
         }
-        
+
     }
-    
+
     public void updateSharedFiles(Collection<OCFile> sharedFiles) {
         cleanSharedFiles();
-        
+
         if (sharedFiles != null) {
             ArrayList<ContentProviderOperation> operations = 
                     new ArrayList<ContentProviderOperation>(sharedFiles.size());
@@ -1282,11 +1317,9 @@ public class FileDataStorageManager {
                     operations.add(
                             ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
                             withValues(cv).
-                            withSelection(
-                                    ProviderTableMeta._ID + "=?", 
-                                    new String[] { String.valueOf(file.getFileId()) }
-                            ).build()
-                    );
+                            withSelection(ProviderTableMeta._ID + "=?",
+                                    new String[]{String.valueOf(file.getFileId())})
+                            .build());
 
                 } else {
                     // adding a new file
@@ -1297,7 +1330,7 @@ public class FileDataStorageManager {
                     );
                 }
             }
-            
+
             // apply operations in batch
             if (operations.size() > 0) {
                 @SuppressWarnings("unused")
@@ -1306,28 +1339,25 @@ public class FileDataStorageManager {
                         " operations to FileContentProvider");
                 try {
                     if (getContentResolver() != null) {
-                        results = getContentResolver().applyBatch(
-                                MainApp.getAuthority(), operations
-                        );
-    
+                        results = getContentResolver().applyBatch(MainApp.getAuthority(), operations);
                     } else {
                         results = getContentProviderClient().applyBatch(operations);
                     }
-    
+
                 } catch (OperationApplicationException e) {
                     Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
-    
+
                 } catch (RemoteException e) {
                     Log_OC.e(TAG, "Exception in batch of operations  " + e.getMessage());
                 }
             }
         }
-        
-    } 
-    
-    public void removeShare(OCShare share){
+
+    }
+
+    public void removeShare(OCShare share) {
         Uri share_uri = ProviderTableMeta.CONTENT_URI_SHARE;
-        String where = ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?" + " AND " + 
+        String where = ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?" + " AND " +
                 ProviderTableMeta.FILE_PATH + "=?";
         String [] whereArgs = new String[]{mAccount.name, share.getPath()};
         if (getContentProviderClient() != null) {
@@ -1337,10 +1367,10 @@ public class FileDataStorageManager {
                 e.printStackTrace();
             }
         } else {
-            getContentResolver().delete(share_uri, where, whereArgs); 
+            getContentResolver().delete(share_uri, where, whereArgs);
         }
     }
-    
+
     public void saveSharesDB(ArrayList<OCShare> shares) {
         saveShares(shares);
 
@@ -1351,7 +1381,7 @@ public class FileDataStorageManager {
             String path = share.getPath();
             if (share.isFolder()) {
                 path = path + FileUtils.PATH_SEPARATOR;
-            }           
+            }
 
             // Update OCFile with data from share: ShareByLink  and publicLink
             OCFile file = getFileByPath(path);
@@ -1360,18 +1390,18 @@ public class FileDataStorageManager {
                     file.setShareByLink(true);
                     sharedFiles.add(file);
                 }
-            } 
+            }
         }
-        
+
         updateSharedFiles(sharedFiles);
     }
 
-    
+
     public void saveSharesInFolder(ArrayList<OCShare> shares, OCFile folder) {
         cleanSharedFilesInFolder(folder);
         ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
         operations = prepareRemoveSharesInFolder(folder, operations);
-        
+
         if (shares != null) {
             // prepare operations to insert or update files to save in the given folder
             for (OCShare share : shares) {
@@ -1415,7 +1445,7 @@ public class FileDataStorageManager {
                 //}
             }
         }
-            
+
         // apply operations in batch
         if (operations.size() > 0) {
             @SuppressWarnings("unused")
@@ -1437,13 +1467,13 @@ public class FileDataStorageManager {
             }
         }
         //}
-        
+
     }
 
     private ArrayList<ContentProviderOperation> prepareRemoveSharesInFolder(
             OCFile folder, ArrayList<ContentProviderOperation> preparedOperations) {
         if (folder != null) {
-            String where = ProviderTableMeta.OCSHARES_PATH + "=?" + " AND " 
+            String where = ProviderTableMeta.OCSHARES_PATH + "=?" + " AND "
                     + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?";
             String [] whereArgs = new String[]{ "", mAccount.name };
 

+ 33 - 32
src/com/owncloud/android/files/FileMenuFilter.java

@@ -39,7 +39,7 @@ import com.owncloud.android.services.OperationsService.OperationsServiceBinder;
 import com.owncloud.android.ui.activity.ComponentsGetter;
 
 /**
- * Filters out the file actions available in a given {@link Menu} for a given {@link OCFile} 
+ * Filters out the file actions available in a given {@link Menu} for a given {@link OCFile}
  * according to the current state of the latest. 
  */
 public class FileMenuFilter {
@@ -48,10 +48,10 @@ public class FileMenuFilter {
     private ComponentsGetter mComponentsGetter;
     private Account mAccount;
     private Context mContext;
-    
+
     /**
      * Constructor
-     * 
+     *
      * @param targetFile        {@link OCFile} target of the action to filter in the {@link Menu}.
      * @param account           ownCloud {@link Account} holding targetFile.
      * @param cg                Accessor to app components, needed to access the
@@ -64,20 +64,20 @@ public class FileMenuFilter {
         mComponentsGetter = cg;
         mContext = context;
     }
-    
-    
+
+
     /**
      * Filters out the file actions available in the passed {@link Menu} taken into account
      * the state of the {@link OCFile} held by the filter.
-     *  
+     *
      * @param menu              Options or context menu to filter.
      */
     public void filter(Menu menu) {
-        List<Integer> toShow = new ArrayList<Integer>();  
-        List<Integer> toHide = new ArrayList<Integer>();    
-        
+        List<Integer> toShow = new ArrayList<Integer>();
+        List<Integer> toHide = new ArrayList<Integer>();
+
         filter(toShow, toHide);
-        
+
         MenuItem item = null;
         for (int i : toShow) {
             item = menu.findItem(i);
@@ -86,7 +86,7 @@ public class FileMenuFilter {
                 item.setEnabled(true);
             }
         }
-        
+
         for (int i : toHide) {
             item = menu.findItem(i);
             if (item != null) {
@@ -99,10 +99,10 @@ public class FileMenuFilter {
 
     /**
      * Performs the real filtering, to be applied in the {@link Menu} by the caller methods.
-     * 
+     *
      * Decides what actions must be shown and hidden.
-     *  
-     * @param toShow            List to save the options that must be shown in the menu. 
+     *
+     * @param toShow            List to save the options that must be shown in the menu.
      * @param toHide            List to save the options that must be shown in the menu.
      */
     private void filter(List<Integer> toShow, List <Integer> toHide) {
@@ -116,71 +116,72 @@ public class FileMenuFilter {
             FileUploaderBinder uploaderBinder = mComponentsGetter.getFileUploaderBinder();
             uploading = (uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile));
         }
-        
+
         /// decision is taken for each possible action on a file in the menu
-        
+
         // DOWNLOAD 
         if (mFile == null || mFile.isDown() || downloading || uploading) {
             toHide.add(R.id.action_download_file);
-            
+
         } else {
             toShow.add(R.id.action_download_file);
         }
-        
+
         // RENAME
         if (mFile == null || downloading || uploading) {
             toHide.add(R.id.action_rename_file);
-            
+
         } else {
             toShow.add(R.id.action_rename_file);
         }
 
-        // MOVE
+        // MOVE & COPY
         if (mFile == null || downloading || uploading) {
             toHide.add(R.id.action_move);
-
+            toHide.add(R.id.action_copy);
         } else {
             toShow.add(R.id.action_move);
+            toShow.add(R.id.action_copy);
         }
-        
+
         // REMOVE
         if (mFile == null || downloading || uploading) {
             toHide.add(R.id.action_remove_file);
-            
+
         } else {
             toShow.add(R.id.action_remove_file);
         }
-        
+
         // OPEN WITH (different to preview!)
         if (mFile == null || mFile.isFolder() || !mFile.isDown() || downloading || uploading) {
             toHide.add(R.id.action_open_file_with);
-            
+
         } else {
             toShow.add(R.id.action_open_file_with);
         }
-        
-        
+
+
         // CANCEL DOWNLOAD
         if (mFile == null || !downloading) {
             toHide.add(R.id.action_cancel_download);
         } else {
             toShow.add(R.id.action_cancel_download);
         }
-        
+
         // CANCEL UPLOAD
         if (mFile == null || !uploading || mFile.isFolder()) {
             toHide.add(R.id.action_cancel_upload);
         } else {
             toShow.add(R.id.action_cancel_upload);
         }
-        
+
         // SYNC FILE CONTENTS
         if (mFile == null || mFile.isFolder() || !mFile.isDown() || downloading || uploading) {
             toHide.add(R.id.action_sync_file);
         } else {
             toShow.add(R.id.action_sync_file);
         }
-        
+
         // SHARE FILE 
         // TODO add check on SHARE available on server side?
         boolean shareAllowed = (mContext != null  &&
@@ -190,7 +191,7 @@ public class FileMenuFilter {
         } else {
             toShow.add(R.id.action_share_file);
         }
-        
+
         // UNSHARE FILE  
         // TODO add check on SHARE available on server side?
         if ( !shareAllowed || (mFile == null || !mFile.isShareByLink())) {
@@ -205,7 +206,7 @@ public class FileMenuFilter {
         } else {
             toShow.add(R.id.action_see_details);
         }
-        
+
         // SEND
         boolean sendAllowed = (mContext != null &&
                 mContext.getString(R.string.send_files_to_other_apps).equalsIgnoreCase("on"));

+ 56 - 39
src/com/owncloud/android/files/FileOperationsHelper.java

@@ -21,8 +21,6 @@
 
 package com.owncloud.android.files;
 
-import org.apache.http.protocol.HTTP;
-
 import android.accounts.Account;
 import android.content.Intent;
 import android.net.Uri;
@@ -35,7 +33,6 @@ import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
-
 import com.owncloud.android.lib.common.network.WebdavUtils;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.lib.resources.status.OwnCloudVersion;
@@ -44,20 +41,22 @@ import com.owncloud.android.services.observer.FileObserverService;
 import com.owncloud.android.ui.activity.FileActivity;
 import com.owncloud.android.ui.dialog.ShareLinkToDialog;
 
+import org.apache.http.protocol.HTTP;
+
 /**
  *
  */
 public class FileOperationsHelper {
 
     private static final String TAG = FileOperationsHelper.class.getName();
-    
-    private static final String FTAG_CHOOSER_DIALOG = "CHOOSER_DIALOG"; 
+
+    private static final String FTAG_CHOOSER_DIALOG = "CHOOSER_DIALOG";
 
     protected FileActivity mFileActivity = null;
 
     /// Identifier of operation in progress which result shouldn't be lost 
     private long mWaitingForOpId = Long.MAX_VALUE;
-    
+
     public FileOperationsHelper(FileActivity fileActivity) {
         mFileActivity = fileActivity;
     }
@@ -67,7 +66,7 @@ public class FileOperationsHelper {
         if (file != null) {
             String storagePath = file.getStoragePath();
             String encodedStoragePath = WebdavUtils.encodePath(storagePath);
-            
+
             Intent intentForSavedMimeType = new Intent(Intent.ACTION_VIEW);
             intentForSavedMimeType.setDataAndType(Uri.parse("file://"+ encodedStoragePath), file.getMimetype());
             intentForSavedMimeType.setFlags(
@@ -94,29 +93,29 @@ public class FileOperationsHelper {
             } else {
                 chooserIntent = Intent.createChooser(intentForSavedMimeType, mFileActivity.getString(R.string.actionbar_open_with));
             }
-            
+
             mFileActivity.startActivity(chooserIntent);
-            
+
         } else {
             Log_OC.wtf(TAG, "Trying to open a NULL OCFile");
         }
     }
-    
-    
+
+
     public void shareFileWithLink(OCFile file) {
-        
+
         if (isSharedSupported()) {
             if (file != null) {
                 String link = "https://fake.url";
                 Intent intent = createShareWithLinkIntent(link);
-                String[] packagesToExclude = new String[] { mFileActivity.getPackageName() };
+                String[] packagesToExclude = new String[]{mFileActivity.getPackageName()};
                 DialogFragment chooserDialog = ShareLinkToDialog.newInstance(intent, packagesToExclude, file);
                 chooserDialog.show(mFileActivity.getSupportFragmentManager(), FTAG_CHOOSER_DIALOG);
-                
+
             } else {
                 Log_OC.wtf(TAG, "Trying to share a NULL OCFile");
             }
-            
+
         } else {
             // Show a Message
             Toast t = Toast.makeText(
@@ -125,13 +124,13 @@ public class FileOperationsHelper {
             t.show();
         }
     }
-    
-    
+
+
     public void shareFileWithLinkToApp(OCFile file, String password, Intent sendIntent) {
         
         if (file != null) {
             mFileActivity.showLoadingDialog();
-            
+
             Intent service = new Intent(mFileActivity, OperationsService.class);
             service.setAction(OperationsService.ACTION_CREATE_SHARE);
             service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
@@ -144,18 +143,18 @@ public class FileOperationsHelper {
             Log_OC.wtf(TAG, "Trying to open a NULL OCFile");
         }
     }
-    
-    
+
+
     private Intent createShareWithLinkIntent(String link) {
         Intent intentToShareLink = new Intent(Intent.ACTION_SEND);
         intentToShareLink.putExtra(Intent.EXTRA_TEXT, link);
         intentToShareLink.setType(HTTP.PLAIN_TEXT_TYPE);
-        return intentToShareLink; 
+        return intentToShareLink;
     }
-    
-    
+
+
     /**
-     *  @return 'True' if the server supports the Share API
+     * @return 'True' if the server supports the Share API
      */
     public boolean isSharedSupported() {
         if (mFileActivity.getAccount() != null) {
@@ -164,10 +163,10 @@ public class FileOperationsHelper {
         }
         return false;
     }
-    
-    
+
+
     public void unshareFileWithLink(OCFile file) {
-        
+
         if (isSharedSupported()) {
             // Unshare the file
             Intent service = new Intent(mFileActivity, OperationsService.class);
@@ -177,15 +176,15 @@ public class FileOperationsHelper {
             mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
             
             mFileActivity.showLoadingDialog();
-            
+
         } else {
             // Show a Message
             Toast t = Toast.makeText(mFileActivity, mFileActivity.getString(R.string.share_link_no_support_share_api), Toast.LENGTH_LONG);
             t.show();
-            
+
         }
     }
-    
+
     public void sendDownloadedFile(OCFile file) {
         if (file != null) {
             String storagePath = file.getStoragePath();
@@ -197,7 +196,7 @@ public class FileOperationsHelper {
             sendIntent.putExtra(Intent.ACTION_SEND, true);      // Send Action
 
             // Show dialog, without the own app
-            String[] packagesToExclude = new String[] { mFileActivity.getPackageName() };
+            String[] packagesToExclude = new String[]{mFileActivity.getPackageName()};
             DialogFragment chooserDialog = ShareLinkToDialog.newInstance(sendIntent, packagesToExclude, file);
             chooserDialog.show(mFileActivity.getSupportFragmentManager(), FTAG_CHOOSER_DIALOG);
 
@@ -205,10 +204,10 @@ public class FileOperationsHelper {
             Log_OC.wtf(TAG, "Trying to send a NULL OCFile");
         }
     }
-    
-    
+
+
     public void syncFile(OCFile file) {
-        
+
         if (!file.isFolder()){
             Intent intent = new Intent(mFileActivity, OperationsService.class);
             intent.setAction(OperationsService.ACTION_SYNC_FILE);
@@ -269,8 +268,8 @@ public class FileOperationsHelper {
         
         mFileActivity.showLoadingDialog();
     }
-    
-    
+
+
     public void createFolder(String remotePath, boolean createFullPath) {
         // Create Folder
         Intent service = new Intent(mFileActivity, OperationsService.class);
@@ -317,8 +316,9 @@ public class FileOperationsHelper {
 
     /**
      * Start move file operation
-     * @param newfile           File where it is going to be moved
-     * @param currentFile       File with the previous info
+     *
+     * @param newfile     File where it is going to be moved
+     * @param currentFile File with the previous info
      */
     public void moveFile(OCFile newfile, OCFile currentFile) {
         // Move files
@@ -332,6 +332,23 @@ public class FileOperationsHelper {
         mFileActivity.showLoadingDialog();
     }
 
+    /**
+     * Start copy file operation
+     *
+     * @param newfile     File where it is going to be moved
+     * @param currentFile File with the previous info
+     */
+    public void copyFile(OCFile newfile, OCFile currentFile) {
+        // Copy files
+        Intent service = new Intent(mFileActivity, OperationsService.class);
+        service.setAction(OperationsService.ACTION_COPY_FILE);
+        service.putExtra(OperationsService.EXTRA_NEW_PARENT_PATH, newfile.getRemotePath());
+        service.putExtra(OperationsService.EXTRA_REMOTE_PATH, currentFile.getRemotePath());
+        service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
+        mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
+
+        mFileActivity.showLoadingDialog();
+    }
 
     public long getOpIdWaitingFor() {
         return mWaitingForOpId;
@@ -341,7 +358,7 @@ public class FileOperationsHelper {
     public void setOpIdWaitingFor(long waitingForOpId) {
         mWaitingForOpId = waitingForOpId;
     }
-    
+
     /**
      *  @return 'True' if the server doesn't need to check forbidden characters
      */

+ 103 - 0
src/com/owncloud/android/operations/CopyFileOperation.java

@@ -0,0 +1,103 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2012-2014 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   as published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package com.owncloud.android.operations;
+
+import android.accounts.Account;
+import android.content.Context;
+
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.lib.resources.files.CopyRemoteFileOperation;
+import com.owncloud.android.operations.common.SyncOperation;
+
+
+/**
+ * Operation copying an {@link OCFile} to a different folder.
+ *
+ * @author David A. Velasco
+ */
+public class CopyFileOperation extends SyncOperation {
+
+    //private static final String TAG = MoveFileOperation.class.getSimpleName();
+
+    private String mSrcPath;
+    private String mTargetParentPath;
+
+    private OCFile mFile;
+
+
+    /**
+     * Constructor
+     *
+     * @param srcPath          Remote path of the {@link OCFile} to move.
+     * @param targetParentPath Path to the folder where the file will be copied into.
+     * @param account          OwnCloud account containing both the file and the target folder
+     */
+    public CopyFileOperation(String srcPath, String targetParentPath, Account account) {
+        mSrcPath = srcPath;
+        mTargetParentPath = targetParentPath;
+        if (!mTargetParentPath.endsWith(OCFile.PATH_SEPARATOR)) {
+            mTargetParentPath += OCFile.PATH_SEPARATOR;
+        }
+
+        mFile = null;
+    }
+
+    /**
+     * Performs the operation.
+     *
+     * @param client Client object to communicate with the remote ownCloud server.
+     */
+    @Override
+    protected RemoteOperationResult run(OwnCloudClient client) {
+        RemoteOperationResult result;
+
+        /// 1. check copy validity
+        if (mTargetParentPath.startsWith(mSrcPath)) {
+            return new RemoteOperationResult(ResultCode.INVALID_COPY_INTO_DESCENDANT);
+        }
+        mFile = getStorageManager().getFileByPath(mSrcPath);
+        if (mFile == null) {
+            return new RemoteOperationResult(ResultCode.FILE_NOT_FOUND);
+        }
+
+        /// 2. remote copy
+        String targetPath = mTargetParentPath + mFile.getFileName();
+        if (mFile.isFolder()) {
+            targetPath += OCFile.PATH_SEPARATOR;
+        }
+        CopyRemoteFileOperation operation = new CopyRemoteFileOperation(
+                mSrcPath,
+                targetPath,
+                false
+        );
+        result = operation.execute(client);
+
+        /// 3. local copy
+        if (result.isSuccess()) {
+            getStorageManager().copyLocalFile(mFile, targetPath);
+        }
+        // TODO handle ResultCode.PARTIAL_COPY_DONE in client Activity, for the moment
+
+        return result;
+    }
+
+
+}

+ 95 - 88
src/com/owncloud/android/services/OperationsService.java

@@ -19,11 +19,21 @@
 
 package com.owncloud.android.services;
 
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.ConcurrentMap;
+import android.accounts.Account;
+import android.accounts.AccountsException;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.app.Service;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.util.Pair;
 
 import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
@@ -42,7 +52,7 @@ import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.lib.resources.shares.ShareType;
 import com.owncloud.android.lib.resources.status.OwnCloudVersion;
 import com.owncloud.android.lib.resources.users.GetRemoteUserNameOperation;
-import com.owncloud.android.operations.common.SyncOperation;
+import com.owncloud.android.operations.CopyFileOperation;
 import com.owncloud.android.operations.CreateFolderOperation;
 import com.owncloud.android.operations.CreateShareOperation;
 import com.owncloud.android.operations.GetServerInfoOperation;
@@ -53,28 +63,18 @@ import com.owncloud.android.operations.RenameFileOperation;
 import com.owncloud.android.operations.SynchronizeFileOperation;
 import com.owncloud.android.operations.SynchronizeFolderOperation;
 import com.owncloud.android.operations.UnshareLinkOperation;
+import com.owncloud.android.operations.common.SyncOperation;
 
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.AccountsException;
-import android.accounts.AuthenticatorException;
-import android.accounts.OperationCanceledException;
-import android.app.Service;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Process;
-import android.util.Pair;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ConcurrentMap;
 
 public class OperationsService extends Service {
-    
+
     private static final String TAG = OperationsService.class.getSimpleName();
-    
+
     public static final String EXTRA_ACCOUNT = "ACCOUNT";
     public static final String EXTRA_SERVER_URL = "SERVER_URL";
     public static final String EXTRA_OAUTH2_QUERY_PARAMETERS = "OAUTH2_QUERY_PARAMETERS";
@@ -90,7 +90,7 @@ public class OperationsService extends Service {
     public static final String EXTRA_PASSWORD_SHARE = "PASSWORD_SHARE";
 
     public static final String EXTRA_COOKIE = "COOKIE";
-    
+
     public static final String ACTION_CREATE_SHARE = "CREATE_SHARE";
     public static final String ACTION_UNSHARE = "UNSHARE";
     public static final String ACTION_GET_SERVER_INFO = "GET_SERVER_INFO";
@@ -102,22 +102,24 @@ public class OperationsService extends Service {
     public static final String ACTION_SYNC_FILE = "SYNC_FILE";
     public static final String ACTION_SYNC_FOLDER = "SYNC_FOLDER";//for the moment, just to download
     public static final String ACTION_MOVE_FILE = "MOVE_FILE";
-    
+    public static final String ACTION_COPY_FILE = "COPY_FILE";
+
     public static final String ACTION_OPERATION_ADDED = OperationsService.class.getName() +
             ".OPERATION_ADDED";
     public static final String ACTION_OPERATION_FINISHED = OperationsService.class.getName() +
             ".OPERATION_FINISHED";
 
 
-    private ConcurrentMap<Integer, Pair<RemoteOperation, RemoteOperationResult>> 
-        mUndispatchedFinishedOperations =
+
+    private ConcurrentMap<Integer, Pair<RemoteOperation, RemoteOperationResult>>
+            mUndispatchedFinishedOperations =
             new ConcurrentHashMap<Integer, Pair<RemoteOperation, RemoteOperationResult>>();
-    
+
     private static class Target {
         public Uri mServerUrl = null;
         public Account mAccount = null;
         public String mCookie = null;
-        
+
         public Target(Account account, Uri serverUrl, String cookie) {
             mAccount = account;
             mServerUrl = serverUrl;
@@ -151,11 +153,11 @@ public class OperationsService extends Service {
         mSyncFolderHandler = new SyncFolderHandler(thread.getLooper(), this);
     }
 
-    
+
     /**
      * Entry point to add a new operation to the queue of operations.
-     * 
-     * New operations are added calling to startService(), resulting in a call to this method. 
+     * <p/>
+     * New operations are added calling to startService(), resulting in a call to this method.
      * This ensures the service will keep on working although the caller activity goes away.
      */
     @Override
@@ -200,8 +202,8 @@ public class OperationsService extends Service {
         // Saving cookies
         try {
             OwnCloudClientManagerFactory.getDefaultSingleton().
-                saveAllClients(this, MainApp.getAccountType());
-            
+                    saveAllClients(this, MainApp.getAccountType());
+
             // TODO - get rid of these exceptions
         } catch (AccountNotFoundException e) {
             e.printStackTrace();
@@ -212,7 +214,7 @@ public class OperationsService extends Service {
         } catch (IOException e) {
             e.printStackTrace();
         }
-        
+
         mUndispatchedFinishedOperations.clear();
 
         mOperationsBinder = null;
@@ -227,8 +229,8 @@ public class OperationsService extends Service {
     }
 
     /**
-     * Provides a binder object that clients can use to perform actions on the queue of operations, 
-     * except the addition of new operations. 
+     * Provides a binder object that clients can use to perform actions on the queue of operations,
+     * except the addition of new operations.
      */
     @Override
     public IBinder onBind(Intent intent) {
@@ -236,7 +238,7 @@ public class OperationsService extends Service {
         return mOperationsBinder;
     }
 
-    
+
     /**
      * Called when ALL the bound clients were unbound.
      */
@@ -248,20 +250,20 @@ public class OperationsService extends Service {
 
 
     /**
-     *  Binder to let client components to perform actions on the queue of operations.
-     * 
-     *  It provides by itself the available operations.
+     * Binder to let client components to perform actions on the queue of operations.
+     * <p/>
+     * It provides by itself the available operations.
      */
     public class OperationsServiceBinder extends Binder /* implements OnRemoteOperationListener */ {
-        
-        /** 
+
+        /**
          * Map of listeners that will be reported about the end of operations from a
          * {@link OperationsServiceBinder} instance
          */
-        private ConcurrentMap<OnRemoteOperationListener, Handler> mBoundListeners = 
+        private final ConcurrentMap<OnRemoteOperationListener, Handler> mBoundListeners =
                 new ConcurrentHashMap<OnRemoteOperationListener, Handler>();
-        
-        private ServiceHandler mServiceHandler = null;   
+
+        private ServiceHandler mServiceHandler = null;
 
         public OperationsServiceBinder(ServiceHandler serviceHandler) {
             mServiceHandler = serviceHandler;
@@ -280,15 +282,15 @@ public class OperationsService extends Service {
 
 
         public void clearListeners() {
-            
+
             mBoundListeners.clear();
         }
 
-        
+
         /**
          * Adds a listener interested in being reported about the end of operations.
-         * 
-         * @param listener          Object to notify about the end of operations.    
+         *
+         * @param listener          Object to notify about the end of operations.
          * @param callbackHandler   {@link Handler} to access the listener without
          *                                         breaking Android threading protection.
          */
@@ -298,15 +300,15 @@ public class OperationsService extends Service {
                 mBoundListeners.put(listener, callbackHandler);
             }
         }
-        
-        
+
+
         /**
          * Removes a listener from the list of objects interested in the being reported about
          * the end of operations.
          * 
          * @param listener      Object to notify about progress of transfer.    
          */
-        public void removeOperationListener (OnRemoteOperationListener listener) {
+        public void removeOperationListener(OnRemoteOperationListener listener) {
             synchronized (mBoundListeners) {
                 mBoundListeners.remove(listener);
             }
@@ -314,8 +316,8 @@ public class OperationsService extends Service {
 
 
         /**
-         * TODO - IMPORTANT: update implementation when more operations are moved into the service 
-         * 
+         * TODO - IMPORTANT: update implementation when more operations are moved into the service
+         *
          * @return  'True' when an operation that enforces the user to wait for completion is
          *          in process.
          */
@@ -343,8 +345,8 @@ public class OperationsService extends Service {
                 return Long.MAX_VALUE;
             }
         }
-        
-        
+
+
         public boolean dispatchResultIfFinished(int operationId,
                                                 OnRemoteOperationListener listener) {
             Pair<RemoteOperation, RemoteOperationResult> undispatched = 
@@ -378,8 +380,8 @@ public class OperationsService extends Service {
 
 
     /**
-     * Operations worker. Performs the pending operations in the order they were requested. 
-     * 
+     * Operations worker. Performs the pending operations in the order they were requested.
+     *
      * Created with the Looper of a new thread, started in {@link OperationsService#onCreate()}. 
      */
     private static class ServiceHandler extends Handler {
@@ -388,8 +390,8 @@ public class OperationsService extends Service {
         
         
         OperationsService mService;
-        
-        
+
+
         private ConcurrentLinkedQueue<Pair<Target, RemoteOperation>> mPendingOperations =
                 new ConcurrentLinkedQueue<Pair<Target, RemoteOperation>>();
         private RemoteOperation mCurrentOperation = null;
@@ -412,7 +414,7 @@ public class OperationsService extends Service {
             Log_OC.d(TAG, "Stopping after command with id " + msg.arg1);
             mService.stopSelf(msg.arg1);
         }
-        
+
         
         /**
          * Performs the next operation in the queue
@@ -475,7 +477,7 @@ public class OperationsService extends Service {
                     } else {
                         result = mCurrentOperation.execute(mOwnCloudClient);
                     }
-                    
+
                 } catch (AccountsException e) {
                     if (mLastTarget.mAccount == null) {
                         Log_OC.e(TAG, "Error while trying to get authorization for a NULL account",
@@ -515,9 +517,9 @@ public class OperationsService extends Service {
         }
 
 
-        
+
     }
-    
+
 
     /**
      * Creates a new operation, as described by operationIntent.
@@ -535,7 +537,7 @@ public class OperationsService extends Service {
             if (!operationIntent.hasExtra(EXTRA_ACCOUNT) && 
                     !operationIntent.hasExtra(EXTRA_SERVER_URL)) {
                 Log_OC.e(TAG, "Not enough information provided in intent");
-                
+
             } else {
                 Account account = operationIntent.getParcelableExtra(EXTRA_ACCOUNT);
                 String serverUrl = operationIntent.getStringExtra(EXTRA_SERVER_URL);
@@ -556,7 +558,7 @@ public class OperationsService extends Service {
                                 ShareType.PUBLIC_LINK,
                                 "", false, password, 1, sendIntent);
                     }
-                    
+
                 } else if (action.equals(ACTION_UNSHARE)) {  // Unshare file
                     String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);
                     if (remotePath.length() > 0) {
@@ -568,7 +570,7 @@ public class OperationsService extends Service {
                 } else if (action.equals(ACTION_GET_SERVER_INFO)) { 
                     // check OC server and get basic information from it
                     operation = new GetServerInfoOperation(serverUrl, OperationsService.this);
-                    
+
                 } else if (action.equals(ACTION_OAUTH2_GET_ACCESS_TOKEN)) {
                     /// GET ACCESS TOKEN to the OAuth server
                     String oauth2QueryParameters =
@@ -578,7 +580,7 @@ public class OperationsService extends Service {
                             getString(R.string.oauth2_redirect_uri),       
                             getString(R.string.oauth2_grant_type),
                             oauth2QueryParameters);
-                    
+
                 } else if (action.equals(ACTION_GET_USER_NAME)) {
                     // Get User Name
                     operation = new GetRemoteUserNameOperation();
@@ -602,7 +604,7 @@ public class OperationsService extends Service {
                     boolean createFullPath = operationIntent.getBooleanExtra(EXTRA_CREATE_FULL_PATH,
                             true);
                     operation = new CreateFolderOperation(remotePath, createFullPath);
-                    
+
                 } else if (action.equals(ACTION_SYNC_FILE)) {
                     // Sync file
                     String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);
@@ -626,9 +628,14 @@ public class OperationsService extends Service {
                     // Move file/folder
                     String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);
                     String newParentPath = operationIntent.getStringExtra(EXTRA_NEW_PARENT_PATH);
-                    operation = new MoveFileOperation(remotePath,newParentPath,account);
+                    operation = new MoveFileOperation(remotePath, newParentPath, account);
+
+                } else if (action.equals(ACTION_COPY_FILE)) {
+                    // Copy file/folder
+                    String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);
+                    String newParentPath = operationIntent.getStringExtra(EXTRA_NEW_PARENT_PATH);
+                    operation = new CopyFileOperation(remotePath, newParentPath, account);
                 }
-                
             }
                 
         } catch (IllegalArgumentException e) {
@@ -642,11 +649,11 @@ public class OperationsService extends Service {
             return null;
         }
     }
-    
+
 
     /**
      * Sends a broadcast when a new operation is added to the queue.
-     * 
+     *
      * Local broadcasts are only delivered to activities in the same process, but can't be
      * done sticky :\
      * 
@@ -656,43 +663,43 @@ public class OperationsService extends Service {
     private void sendBroadcastNewOperation(Target target, RemoteOperation operation) {
         Intent intent = new Intent(ACTION_OPERATION_ADDED);
         if (target.mAccount != null) {
-            intent.putExtra(EXTRA_ACCOUNT, target.mAccount);    
+            intent.putExtra(EXTRA_ACCOUNT, target.mAccount);
         } else {
-            intent.putExtra(EXTRA_SERVER_URL, target.mServerUrl);    
+            intent.putExtra(EXTRA_SERVER_URL, target.mServerUrl);
         }
         //LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
         //lbm.sendBroadcast(intent);
         sendStickyBroadcast(intent);
     }
 
-    
+
     // TODO - maybe add a notification for real start of operations
-    
+
     /**
      * Sends a LOCAL broadcast when an operations finishes in order to the interested activities c
      * an update their view
      * 
      * Local broadcasts are only delivered to activities in the same process.
-     * 
-     * @param target            Account or URL pointing to an OC server.
-     * @param operation         Finished operation.
-     * @param result            Result of the operation.
+     *
+     * @param target    Account or URL pointing to an OC server.
+     * @param operation Finished operation.
+     * @param result    Result of the operation.
      */
     private void sendBroadcastOperationFinished(Target target, RemoteOperation operation,
                                                 RemoteOperationResult result) {
         Intent intent = new Intent(ACTION_OPERATION_FINISHED);
         intent.putExtra(EXTRA_RESULT, result);
         if (target.mAccount != null) {
-            intent.putExtra(EXTRA_ACCOUNT, target.mAccount);    
+            intent.putExtra(EXTRA_ACCOUNT, target.mAccount);
         } else {
-            intent.putExtra(EXTRA_SERVER_URL, target.mServerUrl);    
+            intent.putExtra(EXTRA_SERVER_URL, target.mServerUrl);
         }
         //LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
         //lbm.sendBroadcast(intent);
         sendStickyBroadcast(intent);
     }
 
-    
+
     /**
      * Notifies the currently subscribed listeners about the end of an operation.
      *
@@ -720,9 +727,9 @@ public class OperationsService extends Service {
         }
         if (count == 0) {
             //mOperationResults.put(operation.hashCode(), result);
-            Pair<RemoteOperation, RemoteOperationResult> undispatched = 
+            Pair<RemoteOperation, RemoteOperationResult> undispatched =
                     new Pair<RemoteOperation, RemoteOperationResult>(operation, result);
-            mUndispatchedFinishedOperations.put(operation.hashCode(), undispatched);
+            mUndispatchedFinishedOperations.put(((Runnable) operation).hashCode(), undispatched);
         }
         Log_OC.d(TAG, "Called " + count + " listeners");
     }

+ 4 - 3
src/com/owncloud/android/ui/activity/FileActivity.java

@@ -510,7 +510,7 @@ public class FileActivity extends AppCompatActivity
      */
     private void swapToDefaultAccount() {
         // default to the most recently used account
-        Account newAccount  = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext());
+        Account newAccount = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext());
         if (newAccount == null) {
             /// no account available: force account creation
             createFirstAccount();
@@ -602,7 +602,7 @@ public class FileActivity extends AppCompatActivity
     }
 
     /**
-     * @return  'True' when the Activity is finishing to enforce the setup of a new account.
+     * @return 'True' when the Activity is finishing to enforce the setup of a new account.
      */
     protected boolean isRedirectingToSetupAccount() {
         return mRedirectingToSetupAccount;
@@ -752,6 +752,7 @@ public class FileActivity extends AppCompatActivity
     }
 
 
+
     private void onCreateShareOperationFinish(CreateShareOperation operation,
                                               RemoteOperationResult result) {
         dismissLoadingDialog();
@@ -857,7 +858,7 @@ public class FileActivity extends AppCompatActivity
     /**
      * Dismiss loading dialog
      */
-    public void dismissLoadingDialog(){
+    public void dismissLoadingDialog() {
         Fragment frag = getSupportFragmentManager().findFragmentByTag(DIALOG_WAIT_TAG);
         if (frag != null) {
             LoadingDialog loading = (LoadingDialog) frag;

+ 65 - 7
src/com/owncloud/android/ui/activity/FileDisplayActivity.java

@@ -77,6 +77,7 @@ import com.owncloud.android.lib.common.operations.RemoteOperation;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
 import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.operations.CopyFileOperation;
 import com.owncloud.android.operations.CreateFolderOperation;
 import com.owncloud.android.operations.CreateShareOperation;
 import com.owncloud.android.operations.MoveFileOperation;
@@ -136,6 +137,7 @@ public class FileDisplayActivity extends HookActivity
     public static final int ACTION_SELECT_CONTENT_FROM_APPS = 1;
     public static final int ACTION_SELECT_MULTIPLE_FILES = 2;
     public static final int ACTION_MOVE_FILES = 3;
+    public static final int ACTION_COPY_FILES = 4;
 
     private static final String TAG = FileDisplayActivity.class.getSimpleName();
 
@@ -184,7 +186,7 @@ public class FileDisplayActivity extends HookActivity
 
         // Inflate and set the layout view
         setContentView(R.layout.files);
-        
+
         // Navigation Drawer
         initDrawer();
 
@@ -624,8 +626,7 @@ public class FileDisplayActivity extends HookActivity
                 resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)) {
             requestMultipleUpload(data, resultCode);
 
-        } else if (requestCode == ACTION_MOVE_FILES && resultCode == RESULT_OK) {
-
+        } else if (requestCode == ACTION_MOVE_FILES && resultCode == RESULT_OK){
             final Intent fData = data;
             final int fResultCode = resultCode;
             getHandler().postDelayed(
@@ -638,6 +639,20 @@ public class FileDisplayActivity extends HookActivity
                     DELAY_TO_REQUEST_OPERATION_ON_ACTIVITY_RESULTS
             );
 
+        } else if (requestCode == ACTION_COPY_FILES && resultCode == RESULT_OK) {
+
+            final Intent fData = data;
+            final int fResultCode = resultCode;
+            getHandler().postDelayed(
+                    new Runnable() {
+                        @Override
+                        public void run() {
+                            requestCopyOperation(fData, fResultCode);
+                        }
+                    },
+                    DELAY_TO_REQUEST_OPERATION_ON_ACTIVITY_RESULTS
+            );
+
         } else {
             super.onActivityResult(requestCode, resultCode, data);
         }
@@ -753,6 +768,18 @@ public class FileDisplayActivity extends HookActivity
         getFileOperationsHelper().moveFile(folderToMoveAt, targetFile);
     }
 
+    /**
+     * Request the operation for copying the file/folder from one path to another
+     *
+     * @param data       Intent received
+     * @param resultCode Result code received
+     */
+    private void requestCopyOperation(Intent data, int resultCode) {
+        OCFile folderToMoveAt = data.getParcelableExtra(FolderPickerActivity.EXTRA_FOLDER);
+        OCFile targetFile = data.getParcelableExtra(FolderPickerActivity.EXTRA_FILE);
+        getFileOperationsHelper().copyFile(folderToMoveAt, targetFile);
+    }
+
     @Override
     public void onBackPressed() {
         if (!isDrawerOpen()){
@@ -824,6 +851,7 @@ public class FileDisplayActivity extends HookActivity
         downloadIntentFilter.addAction(FileDownloader.getDownloadFinishMessage());
         mDownloadFinishReceiver = new DownloadFinishReceiver();
         registerReceiver(mDownloadFinishReceiver, downloadIntentFilter);
+
         Log_OC.v(TAG, "onResume() end");
 
     }
@@ -920,6 +948,7 @@ public class FileDisplayActivity extends HookActivity
                                 
                         if (RefreshFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED.
                                     equals(event) &&/// TODO refactor and make common
+
                                 synchResult != null && !synchResult.isSuccess() &&
                                 (synchResult.getCode() == ResultCode.UNAUTHORIZED ||
                                         synchResult.isIdPRedirection() ||
@@ -1021,6 +1050,7 @@ public class FileDisplayActivity extends HookActivity
                 if (sameAccount && isDescendant) {
                     refreshListOfFilesFragment();
                 }
+
                 boolean uploadWasFine = intent.getBooleanExtra(FileUploader.EXTRA_UPLOAD_RESULT,
                         false);
                 boolean renamedInUpload = getFile().getRemotePath().
@@ -1312,10 +1342,12 @@ public class FileDisplayActivity extends HookActivity
 
         } else if (operation instanceof MoveFileOperation) {
             onMoveFileOperationFinish((MoveFileOperation) operation, result);
+
+        } else if (operation instanceof CopyFileOperation) {
+            onCopyFileOperationFinish((CopyFileOperation) operation, result);
         }
 
     }
-    
     private void onCreateShareOperationFinish(CreateShareOperation operation,
                                               RemoteOperationResult result) {
         if (result.isSuccess()) {
@@ -1348,8 +1380,9 @@ public class FileDisplayActivity extends HookActivity
                 } else if (details instanceof PreviewTextFragment) {
                     // Refresh  OCFile of the fragment
                     ((PreviewTextFragment) details).updateFile(file);
-                } else
+                } else {
                     showDetails(file);
+                }
             }
             invalidateOptionsMenu();
         }
@@ -1365,6 +1398,7 @@ public class FileDisplayActivity extends HookActivity
     private void onRemoveFileOperationFinish(RemoveFileOperation operation,
                                              RemoteOperationResult result) {
         dismissLoadingDialog();
+
         Toast msg = Toast.makeText(this,
                 ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
                 Toast.LENGTH_LONG);
@@ -1419,6 +1453,30 @@ public class FileDisplayActivity extends HookActivity
         }
     }
 
+    /**
+     * Updates the view associated to the activity after the finish of an operation trying to copy a
+     * file.
+     *
+     * @param operation Copy operation performed.
+     * @param result    Result of the copy operation.
+     */
+    private void onCopyFileOperationFinish(CopyFileOperation operation, RemoteOperationResult result) {
+        if (result.isSuccess()) {
+            dismissLoadingDialog();
+            refreshListOfFilesFragment();
+        } else {
+            dismissLoadingDialog();
+            try {
+                Toast msg = Toast.makeText(FileDisplayActivity.this,
+                        ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
+                        Toast.LENGTH_LONG);
+                msg.show();
+
+            } catch (NotFoundException e) {
+                Log_OC.e(TAG, "Error while trying to show fail message ", e);
+            }
+        }
+    }
 
     /**
      * Updates the view associated to the activity after the finish of an operation trying to rename
@@ -1458,7 +1516,7 @@ public class FileDisplayActivity extends HookActivity
                     }
                 }
             }
-            
+
             if (getStorageManager().getFileById(renamedFile.getParentId()).equals(getCurrentDir())){
                 refreshListOfFilesFragment();
             }
@@ -1641,7 +1699,7 @@ public class FileDisplayActivity extends HookActivity
 
     /**
      * Stars the preview of an already down media {@link OCFile}.
-     * 
+     *
      * @param file                      Media {@link OCFile} to preview.
      * @param startPlaybackPosition     Media position where the playback will be started,
      *                                  in milliseconds.

+ 3 - 3
src/com/owncloud/android/ui/fragment/ExtendedListFragment.java

@@ -71,8 +71,8 @@ public class ExtendedListFragment extends Fragment
     private ArrayList<Integer> mTops;
     private int mHeightCell = 0;
 
-    private OnEnforceableRefreshListener mOnRefreshListener = null;
-    
+    private SwipeRefreshLayout.OnRefreshListener mOnRefreshListener = null;
+
     protected AbsListView mCurrentListView;
     private ExtendedListView mListView;
     private View mListFooterView;
@@ -352,7 +352,7 @@ public class ExtendedListFragment extends Fragment
         mRefreshEmptyLayout.setRefreshing(false);
 
         if (mOnRefreshListener != null) {
-            mOnRefreshListener.onRefresh(ignoreETag);
+            mOnRefreshListener.onRefresh();
         }
     }
 

+ 75 - 63
src/com/owncloud/android/ui/fragment/FileDetailFragment.java

@@ -21,10 +21,7 @@
  */
 package com.owncloud.android.ui.fragment;
 
-import java.lang.ref.WeakReference;
-
 import android.accounts.Account;
-import android.content.Intent;
 import android.os.Bundle;
 import android.view.LayoutInflater;
 import android.view.Menu;
@@ -46,13 +43,14 @@ import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
 import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
 import com.owncloud.android.lib.common.utils.Log_OC;
-import com.owncloud.android.services.observer.FileObserverService;
 import com.owncloud.android.ui.activity.FileActivity;
 import com.owncloud.android.ui.activity.FileDisplayActivity;
 import com.owncloud.android.ui.dialog.RemoveFileDialogFragment;
 import com.owncloud.android.ui.dialog.RenameFileDialogFragment;
 import com.owncloud.android.utils.DisplayUtils;
 
+import java.lang.ref.WeakReference;
+
 
 /**
  * This Fragment is used to display the details about a file.
@@ -62,9 +60,9 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
     private int mLayout;
     private View mView;
     private Account mAccount;
-    
+
     public ProgressListener mProgressListener;
-    
+
     private static final String TAG = FileDetailFragment.class.getSimpleName();
     public static final String FTAG_CONFIRMATION = "REMOVE_CONFIRMATION_FRAGMENT";
     public static final String FTAG_RENAME_FILE = "RENAME_FILE_FRAGMENT";
@@ -103,14 +101,14 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
         mLayout = R.layout.file_details_empty;
         mProgressListener = null;
     }
-    
+
 
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setHasOptionsMenu(true);
     }
-    
+
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
@@ -120,14 +118,14 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
         mAccount = getArguments().getParcelable(ARG_ACCOUNT);
 
         if (savedInstanceState != null) {
-            setFile((OCFile)savedInstanceState.getParcelable(FileActivity.EXTRA_FILE));
+            setFile((OCFile) savedInstanceState.getParcelable(FileActivity.EXTRA_FILE));
             mAccount = savedInstanceState.getParcelable(FileActivity.EXTRA_ACCOUNT);
         }
-        
-        if(getFile() != null && mAccount != null) {
+
+        if (getFile() != null && mAccount != null) {
             mLayout = R.layout.file_details_fragment;
         }
-        
+
         mView = inflater.inflate(mLayout, null);
         
         if (mLayout == R.layout.file_details_fragment) {
@@ -154,20 +152,20 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
         super.onStart();
         listenForTransferProgress();
     }
-    
+
     @Override
     public void onStop() {
         leaveTransferProgress();
         super.onStop();
     }
 
-    
+
     @Override
     public View getView() {
         return super.getView() == null ? mView : super.getView();
     }
 
-    
+
     /**
      * {@inheritDoc}
      */
@@ -175,16 +173,16 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
         super.onCreateOptionsMenu(menu, inflater);
         inflater.inflate(R.menu.file_actions_menu, menu);
-   }
+    }
+
 
-    
     /**
      * {@inheritDoc}
      */
     @Override
-    public void onPrepareOptionsMenu (Menu menu) {
+    public void onPrepareOptionsMenu(Menu menu) {
         super.onPrepareOptionsMenu(menu);
-        
+
         if (mContainerActivity.getStorageManager() != null) {
             FileMenuFilter mf = new FileMenuFilter(
                 getFile(),
@@ -194,7 +192,7 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
             );
             mf.filter(menu);
         }
-        
+
         // additional restriction for this fragment 
         MenuItem item = menu.findItem(R.id.action_see_details);
         if (item != null) {
@@ -208,9 +206,16 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
             item.setVisible(false);
             item.setEnabled(false);
         }
+
+        // additional restriction for this fragment
+        item = menu.findItem(R.id.action_copy);
+        if (item != null) {
+            item.setVisible(false);
+            item.setEnabled(false);
+        }
     }
 
-    
+
     /**
      * {@inheritDoc}
      */
@@ -241,10 +246,10 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
             }
             case R.id.action_cancel_download:
             case R.id.action_cancel_upload: {
-                ((FileDisplayActivity)mContainerActivity).cancelTransference(getFile());
+                ((FileDisplayActivity) mContainerActivity).cancelTransference(getFile());
                 return true;
             }
-            case R.id.action_download_file: 
+            case R.id.action_download_file:
             case R.id.action_sync_file: {
                 mContainerActivity.getFileOperationsHelper().syncFile(getFile());
                 return true;
@@ -253,9 +258,10 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
                 // Obtain the file
                 if (!getFile().isDown()) {  // Download the file                    
                     Log_OC.d(TAG, getFile().getRemotePath() + " : File must be downloaded");
-                    ((FileDisplayActivity)mContainerActivity).startDownloadForSending(getFile());
-                    
-                } else {
+                    ((FileDisplayActivity) mContainerActivity).startDownloadForSending(getFile());
+
+                }
+                else {
                     mContainerActivity.getFileOperationsHelper().sendDownloadedFile(getFile());
                 }
                 return true;
@@ -282,7 +288,7 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
                 break;
             }
             case R.id.fdCancelBtn: {
-                ((FileDisplayActivity)mContainerActivity).cancelTransference(getFile());
+                ((FileDisplayActivity) mContainerActivity).cancelTransference(getFile());
                 break;
             }
             default:
@@ -293,17 +299,17 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
 
     /**
      * 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.
+     *
+     * @return True when the fragment was created with the empty layout.
      */
     public boolean isEmpty() {
         return (mLayout == R.layout.file_details_empty || getFile() == null || mAccount == null);
     }
 
-    
+
     /**
      * 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) {
@@ -314,14 +320,13 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
 
     /**
      * Updates the view with all relevant details about that file.
+     * <p/>
+     * TODO Remove parameter when the transferring state of files is kept in database.
      *
-     * TODO Remove parameter when the transferring state of files is kept in database. 
-     * 
-     * @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 whole file from the database
+     * @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 whole file from the database
      */
     public void updateFileDetails(boolean transferring, boolean refresh) {
         if (readyToShow()) {
@@ -330,7 +335,7 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
                 setFile(storageManager.getFileByPath(getFile().getRemotePath()));
             }
             OCFile file = getFile();
-            
+
             // set file details
             setFilename(file.getFileName());
             setFiletype(file.getMimetype(), file.getFileName());
@@ -349,7 +354,7 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
                     (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file))
                     ) {
                 setButtonsForTransferring();
-                
+
             } else if (file.isDown()) {
                 
                 setButtonsForDown();
@@ -362,25 +367,27 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
         }
         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
+     *
+     * @return 'True' when the fragment is ready to show details of a file
      */
     private boolean readyToShow() {
-        return (getFile() != null && mAccount != null && mLayout == R.layout.file_details_fragment);        
+        return (getFile() != 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)
+        if (tv != null) {
             tv.setText(filename);
+        }
     }
 
     /**
@@ -402,25 +409,28 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
 
     /**
      * 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)
+        if (tv != null) {
             tv.setText(DisplayUtils.bytesToHumanReadable(filesize));
+        }
     }
-    
+
     /**
      * Updates the time that the file was last modified
+     *
      * @param milliseconds Unix time to set
      */
-    private void setTimeModified(long milliseconds){
+    private void setTimeModified(long milliseconds) {
         TextView tv = (TextView) getView().findViewById(R.id.fdModified);
-        if(tv != null){
+        if (tv != null) {
             tv.setText(DisplayUtils.unixTimeToHumanReadable(milliseconds));
         }
     }
-    
+
     /**
      * Enables or disables buttons for a file being downloaded
      */
@@ -431,21 +441,24 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
             
             // show the progress bar for the transfer
             getView().findViewById(R.id.fdProgressBlock).setVisibility(View.VISIBLE);
-            TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText);
+            TextView progressText = (TextView) getView().findViewById(R.id.fdProgressText);
             progressText.setVisibility(View.VISIBLE);
             FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
             FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
             //if (getFile().isDownloading()) {
             if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, getFile())) {
                 progressText.setText(R.string.downloader_download_in_progress_ticker);
-            } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, getFile())) {
-                progressText.setText(R.string.uploader_upload_in_progress_ticker);
+            }
+            else {
+                if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, getFile())) {
+                    progressText.setText(R.string.uploader_upload_in_progress_ticker);
+                }
             }
         }
     }
 
     /**
-     * Enables or disables buttons for a file locally available 
+     * Enables or disables buttons for a file locally available
      */
     private void setButtonsForDown() {
         if (!isEmpty()) {
@@ -453,13 +466,13 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
             
             // hides the progress bar
             getView().findViewById(R.id.fdProgressBlock).setVisibility(View.GONE);
-            TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText);
+            TextView progressText = (TextView) getView().findViewById(R.id.fdProgressText);
             progressText.setVisibility(View.GONE);
         }
     }
 
     /**
-     * Enables or disables buttons for a file not locally available 
+     * Enables or disables buttons for a file not locally available
      */
     private void setButtonsForRemote() {
         if (!isEmpty()) {
@@ -467,11 +480,11 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
             
             // hides the progress bar
             getView().findViewById(R.id.fdProgressBlock).setVisibility(View.GONE);
-            TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText);
+            TextView progressText = (TextView) getView().findViewById(R.id.fdProgressText);
             progressText.setVisibility(View.GONE);
         }
     }
-    
+
 
     public void listenForTransferProgress() {
         if (mProgressListener != null) {
@@ -485,8 +498,8 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
             }
         }
     }
-    
-    
+
+
     public void leaveTransferProgress() {
         if (mProgressListener != null) {
             if (mContainerActivity.getFileDownloaderBinder() != null) {
@@ -501,7 +514,6 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
     }
 
 
-    
     /**
      * Helper class responsible for updating the progress bar shown for file uploading or
      * downloading
@@ -509,11 +521,11 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
     private class ProgressListener implements OnDatatransferProgressListener {
         int mLastPercent = 0;
         WeakReference<ProgressBar> mProgressBar = null;
-        
+
         ProgressListener(ProgressBar progressBar) {
             mProgressBar = new WeakReference<ProgressBar>(progressBar);
         }
-        
+
         @Override
         public void onTransferProgress(long progressRate, long totalTransferredSoFar,
                                        long totalToTransfer, String filename) {

+ 56 - 49
src/com/owncloud/android/ui/fragment/OCFileListFragment.java

@@ -22,8 +22,6 @@
  */
 package com.owncloud.android.ui.fragment;
 
-import java.io.File;
-
 import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
@@ -55,13 +53,14 @@ import com.owncloud.android.ui.dialog.RemoveFileDialogFragment;
 import com.owncloud.android.ui.dialog.RenameFileDialogFragment;
 import com.owncloud.android.ui.preview.PreviewImageFragment;
 import com.owncloud.android.ui.preview.PreviewMediaFragment;
-import com.owncloud.android.utils.DialogMenuItem;
 import com.owncloud.android.utils.FileStorageUtils;
 import com.owncloud.android.ui.preview.PreviewTextFragment;
 
+import java.io.File;
+
 /**
  * A Fragment that lists all files and folders in a given path.
- * 
+ *
  * TODO refactor to get rid of direct dependency on FileDisplayActivity
  */
 public class OCFileListFragment extends ExtendedListFragment implements FileActionsDialogFragment.FileActionsDialogFragmentListener {
@@ -70,14 +69,14 @@ public class OCFileListFragment extends ExtendedListFragment implements FileActi
 
     private static final String MY_PACKAGE = OCFileListFragment.class.getPackage() != null ?
             OCFileListFragment.class.getPackage().getName() : "com.owncloud.android.ui.fragment";
-            
+
     public final static String ARG_JUST_FOLDERS = MY_PACKAGE + ".JUST_FOLDERS";
     public final static String ARG_ALLOW_CONTEXTUAL_ACTIONS = MY_PACKAGE + ".ALLOW_CONTEXTUAL";
-            
+
     private static final String KEY_FILE = MY_PACKAGE + ".extra.FILE";
 
     private FileFragment.ContainerActivity mContainerActivity;
-   
+
     private OCFile mFile = null;
     private FileListListAdapter mAdapter;
     private boolean mJustFolders;
@@ -95,21 +94,21 @@ public class OCFileListFragment extends ExtendedListFragment implements FileActi
         Log_OC.e(TAG, "onAttach");
         try {
             mContainerActivity = (FileFragment.ContainerActivity) activity;
-            
+
         } catch (ClassCastException e) {
-            throw new ClassCastException(activity.toString() + " must implement " + 
+            throw new ClassCastException(activity.toString() + " must implement " +
                     FileFragment.ContainerActivity.class.getSimpleName());
         }
         try {
             setOnRefreshListener((OnEnforceableRefreshListener) activity);
             
         } catch (ClassCastException e) {
-            throw new ClassCastException(activity.toString() + " must implement " + 
+            throw new ClassCastException(activity.toString() + " must implement " +
                     SwipeRefreshLayout.OnRefreshListener.class.getSimpleName());
         }
     }
 
-    
+
     @Override
     public void onDetach() {
         setOnRefreshListener(null);
@@ -141,7 +140,7 @@ public class OCFileListFragment extends ExtendedListFragment implements FileActi
                 mJustFolders,
                 getActivity(),
                 mContainerActivity
-                );
+        );
         setListAdapter(mAdapter);
 
         registerLongClickListener();
@@ -204,31 +203,31 @@ public class OCFileListFragment extends ExtendedListFragment implements FileActi
      * Saves the current listed folder.
      */
     @Override
-    public void onSaveInstanceState (Bundle outState) {
+    public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
         outState.putParcelable(KEY_FILE, mFile);
     }
-    
+
     /**
      * Call this, when the user presses the up button.
-     * 
+     *
      * Tries to move up the current folder one level. If the parent folder was removed from the
      * database, it continues browsing up until finding an existing folders.
-     * 
+     * <p/>
      * return       Count of folder levels browsed up.
      */
     public int onBrowseUp() {
         OCFile parentDir = null;
         int moveCount = 0;
-        
-        if(mFile != null){
+
+        if (mFile != null) {
             FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
-            
+
             String parentPath = null;
             if (mFile.getParentId() != FileDataStorageManager.ROOT_PARENT_ID) {
                 parentPath = new File(mFile.getRemotePath()).getParent();
-                parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : 
-                	parentPath + OCFile.PATH_SEPARATOR;
+                parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath :
+                        parentPath + OCFile.PATH_SEPARATOR;
                 parentDir = storageManager.getFileByPath(parentPath);
                 moveCount++;
             } else {
@@ -236,8 +235,8 @@ public class OCFileListFragment extends ExtendedListFragment implements FileActi
             }
             while (parentDir == null) {
                 parentPath = new File(parentPath).getParent();
-                parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : 
-                	parentPath + OCFile.PATH_SEPARATOR;
+                parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath :
+                        parentPath + OCFile.PATH_SEPARATOR;
                 parentDir = storageManager.getFileByPath(parentPath);
                 moveCount++;
             }   // exit is granted because storageManager.getFileByPath("/") never returns null
@@ -247,20 +246,20 @@ public class OCFileListFragment extends ExtendedListFragment implements FileActi
             listDirectory(mFile /*, MainApp.getOnlyOnDevice()*/);
 
             onRefresh(false);
-            
+
             // restore index and top position
             restoreIndexAndTopPosition();
-            
+
         }   // else - should never happen now
-   
+
         return moveCount;
     }
-    
+
     @Override
     public void onItemClick(AdapterView<?> l, View v, int position, long id) {
         OCFile file = (OCFile) mAdapter.getItem(position);
         if (file != null) {
-            if (file.isFolder()) { 
+            if (file.isFolder()) {
                 // update state and view of this fragment
                 // TODO Enable when "On Device" is recovered ?
                 listDirectory(file/*, MainApp.getOnlyOnDevice()*/);
@@ -268,7 +267,7 @@ public class OCFileListFragment extends ExtendedListFragment implements FileActi
                 mContainerActivity.onBrowsedDownTo(file);
                 // save index and top position
                 saveIndexAndTopPosition(position);
-                
+
             } else { /// Click on a file
                 if (PreviewImageFragment.canBePreviewed(file)) {
                     // preview image - it handles the download, if needed
@@ -278,39 +277,39 @@ public class OCFileListFragment extends ExtendedListFragment implements FileActi
                 } else if (file.isDown()) {
                     if (PreviewMediaFragment.canBePreviewed(file)) {
                         // media preview
-                        ((FileDisplayActivity)mContainerActivity).startMediaPreview(file, 0, true);
+                        ((FileDisplayActivity) mContainerActivity).startMediaPreview(file, 0, true);
                     } else {
                         mContainerActivity.getFileOperationsHelper().openFile(file);
                     }
-                    
+
                 } else {
                     // automatic download, preview on finish
-                    ((FileDisplayActivity)mContainerActivity).startDownloadForPreview(file);
+                    ((FileDisplayActivity) mContainerActivity).startDownloadForPreview(file);
                 }
-                    
+
             }
-            
+
         } else {
             Log_OC.d(TAG, "Null object in ListAdapter!!");
         }
-        
+
     }
-    
+
     /**
      * {@inheritDoc}
      */
     @Override
-    public void onCreateContextMenu (
+    public void onCreateContextMenu(
             ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
         Bundle args = getArguments();
-        boolean allowContextualActions = 
-                (args == null) ? true : args.getBoolean(ARG_ALLOW_CONTEXTUAL_ACTIONS, true); 
+        boolean allowContextualActions =
+                (args == null) ? true : args.getBoolean(ARG_ALLOW_CONTEXTUAL_ACTIONS, true);
         if (allowContextualActions) {
             MenuInflater inflater = getActivity().getMenuInflater();
             inflater.inflate(R.menu.file_actions_menu, menu);
             AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
             OCFile targetFile = (OCFile) mAdapter.getItem(info.position);
-            
+
             if (mContainerActivity.getStorageManager() != null) {
                 FileMenuFilter mf = new FileMenuFilter(
                     targetFile,
@@ -405,6 +404,13 @@ public class OCFileListFragment extends ExtendedListFragment implements FileActi
                 mContainerActivity.getFileOperationsHelper().toggleFavorite(mTargetFile, false);
                 return true;
             }
+            case R.id.action_copy:
+                Intent action = new Intent(getActivity(), FolderPickerActivity.class);
+
+                // Pass mTargetFile that contains info of selected file/folder
+                action.putExtra(FolderPickerActivity.EXTRA_FILE, mTargetFile);
+                getActivity().startActivityForResult(action, FileDisplayActivity.ACTION_COPY_FILES);
+                return true;
             default:
                 return false;
         }
@@ -428,12 +434,13 @@ public class OCFileListFragment extends ExtendedListFragment implements FileActi
     /**
      * Use this to query the {@link OCFile} that is currently
      * being displayed by this fragment
+     *
      * @return The currently viewed OCFile
      */
-    public OCFile getCurrentFile(){
+    public OCFile getCurrentFile() {
         return mFile;
     }
-    
+
     /**
      * Calls {@link OCFileListFragment#listDirectory(OCFile)} with a null parameter
      */
@@ -447,12 +454,12 @@ public class OCFileListFragment extends ExtendedListFragment implements FileActi
         // TODO Enable when "On Device" is recovered ?
         listDirectory(getCurrentFile()/*, MainApp.getOnlyOnDevice()*/);
     }
-    
+
     /**
      * Lists the given directory on the view. When the input parameter is null,
      * it will either refresh the last known directory. list the root
      * if there never was a directory.
-     * 
+     *
      * @param directory File to be listed
      */
     public void listDirectory(OCFile directory/*, boolean onlyOnDevice*/) {
@@ -460,18 +467,18 @@ public class OCFileListFragment extends ExtendedListFragment implements FileActi
         if (storageManager != null) {
 
             // Check input parameters for null
-            if(directory == null){
-                if(mFile != null){
+            if (directory == null) {
+                if (mFile != null) {
                     directory = mFile;
                 } else {
                     directory = storageManager.getFileByPath("/");
                     if (directory == null) return; // no files, wait for sync
                 }
             }
-        
-        
+
+
             // If that's not a directory -> List its parent
-            if(!directory.isFolder()){
+            if (!directory.isFolder()) {
                 Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
                 directory = storageManager.getFileById(directory.getParentId());
             }

+ 40 - 38
src/com/owncloud/android/ui/preview/FileDownloadFragment.java

@@ -59,9 +59,9 @@ public class FileDownloadFragment extends FileFragment implements OnClickListene
 
     public ProgressListener mProgressListener;
     private boolean mListening;
-    
+
     private static final String TAG = FileDownloadFragment.class.getSimpleName();
-    
+
     private boolean mIgnoreFirstSavedState;
     private boolean mError;
 
@@ -118,19 +118,20 @@ public class FileDownloadFragment extends FileFragment implements OnClickListene
         mIgnoreFirstSavedState = args.getBoolean(ARG_IGNORE_FIRST);
         mAccount = args.getParcelable(ARG_ACCOUNT);
     }
-    
+
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
+                             Bundle savedInstanceState) {
         super.onCreateView(inflater, container, savedInstanceState);
-        
+
         if (savedInstanceState != null) {
             if (!mIgnoreFirstSavedState) {
-                setFile((OCFile)savedInstanceState.getParcelable(FileDownloadFragment.EXTRA_FILE));
+                setFile((OCFile) savedInstanceState.getParcelable(FileDownloadFragment.EXTRA_FILE));
                 mAccount = savedInstanceState.getParcelable(FileDownloadFragment.EXTRA_ACCOUNT);
                 mError = savedInstanceState.getBoolean(FileDownloadFragment.EXTRA_ERROR);
-            } else {
+            }
+            else {
                 mIgnoreFirstSavedState = false;
             }
         }
@@ -140,7 +141,7 @@ public class FileDownloadFragment extends FileFragment implements OnClickListene
         ProgressBar progressBar = (ProgressBar)mView.findViewById(R.id.progressBar);
         DisplayUtils.colorPreLollipopHorizontalProgressBar(progressBar);
         mProgressListener = new ProgressListener(progressBar);
-        
+
         (mView.findViewById(R.id.cancelBtn)).setOnClickListener(this);
         
         (mView.findViewById(R.id.fileDownloadLL)).setOnClickListener(new OnClickListener() {
@@ -152,13 +153,14 @@ public class FileDownloadFragment extends FileFragment implements OnClickListene
 
         if (mError) {
             setButtonsForRemote();
-        } else {
+        }
+        else {
             setButtonsForTransferring();
         }
-        
+
         return mView;
     }
-    
+
 
     @Override
     public void onSaveInstanceState(Bundle outState) {
@@ -173,7 +175,7 @@ public class FileDownloadFragment extends FileFragment implements OnClickListene
         super.onStart();
         listenForTransferProgress();
     }
-    
+
     @Override
     public void onResume() {
         super.onResume();
@@ -185,19 +187,19 @@ public class FileDownloadFragment extends FileFragment implements OnClickListene
         super.onPause();
     }
 
-    
+
     @Override
     public void onStop() {
         leaveTransferProgress();
         super.onStop();
     }
-    
+
     @Override
     public void onDestroy() {
         super.onDestroy();
     }
-    
-    
+
+
     @Override
     public View getView() {
         if (!mListening) {
@@ -206,7 +208,7 @@ public class FileDownloadFragment extends FileFragment implements OnClickListene
         return super.getView() == null ? mView : super.getView();
     }
 
-    
+
     @Override
     public void onClick(View v) {
         switch (v.getId()) {
@@ -220,53 +222,52 @@ public class FileDownloadFragment extends FileFragment implements OnClickListene
         }
     }
 
-    
+
     /**
      * Enables or disables buttons for a file being downloaded
      */
     private void setButtonsForTransferring() {
         getView().findViewById(R.id.cancelBtn).setVisibility(View.VISIBLE);
-    
+
         // show the progress bar for the transfer
         getView().findViewById(R.id.progressBar).setVisibility(View.VISIBLE);
-        TextView progressText = (TextView)getView().findViewById(R.id.progressText);
+        TextView progressText = (TextView) getView().findViewById(R.id.progressText);
         progressText.setText(R.string.downloader_download_in_progress_ticker);
         progressText.setVisibility(View.VISIBLE);
-                
+
         // hides the error icon
         getView().findViewById(R.id.errorText).setVisibility(View.GONE);
         getView().findViewById(R.id.error_image).setVisibility(View.GONE);
     }
-    
 
     /**
-     * Enables or disables buttons for a file locally available 
+     * Enables or disables buttons for a file locally available
      */
     private void setButtonsForDown() {
         getView().findViewById(R.id.cancelBtn).setVisibility(View.GONE);
-    
+
         // hides the progress bar
         getView().findViewById(R.id.progressBar).setVisibility(View.GONE);
-        
+
         // updates the text message
-        TextView progressText = (TextView)getView().findViewById(R.id.progressText);
+        TextView progressText = (TextView) getView().findViewById(R.id.progressText);
         progressText.setText(R.string.common_loading);
         progressText.setVisibility(View.VISIBLE);
-        
+
         // hides the error icon
         getView().findViewById(R.id.errorText).setVisibility(View.GONE);
         getView().findViewById(R.id.error_image).setVisibility(View.GONE);
     }
 
-    
+
     /**
-     * Enables or disables buttons for a file not locally available 
-     * 
+     * Enables or disables buttons for a file not locally available
+     * <p/>
      * Currently, this is only used when a download was failed
      */
     private void setButtonsForRemote() {
         getView().findViewById(R.id.cancelBtn).setVisibility(View.GONE);
-        
+
         // hides the progress bar and message
         getView().findViewById(R.id.progressBar).setVisibility(View.GONE);
         getView().findViewById(R.id.progressText).setVisibility(View.GONE);
@@ -275,7 +276,7 @@ public class FileDownloadFragment extends FileFragment implements OnClickListene
         getView().findViewById(R.id.errorText).setVisibility(View.VISIBLE);
         getView().findViewById(R.id.error_image).setVisibility(View.VISIBLE);
     }
-    
+
 
     public void listenForTransferProgress() {
         if (mProgressListener != null && !mListening) {
@@ -288,8 +289,8 @@ public class FileDownloadFragment extends FileFragment implements OnClickListene
             }
         }
     }
-    
-    
+
+
     public void leaveTransferProgress() {
         if (mProgressListener != null) {
             if (mContainerActivity.getFileDownloaderBinder() != null) {
@@ -308,11 +309,11 @@ public class FileDownloadFragment extends FileFragment implements OnClickListene
     private class ProgressListener implements OnDatatransferProgressListener {
         int mLastPercent = 0;
         WeakReference<ProgressBar> mProgressBar = null;
-        
+
         ProgressListener(ProgressBar progressBar) {
             mProgressBar = new WeakReference<ProgressBar>(progressBar);
         }
-        
+
         @Override
         public void onTransferProgress(
                 long progressRate, long totalTransferredSoFar, long totalToTransfer, String filename
@@ -333,8 +334,9 @@ public class FileDownloadFragment extends FileFragment implements OnClickListene
 
     public void setError(boolean error) {
         mError = error;
-    };
-    
+    }
+
+    ;
 
 
 }

+ 51 - 43
src/com/owncloud/android/ui/preview/PreviewImageFragment.java

@@ -75,11 +75,11 @@ public class PreviewImageFragment extends FileFragment {
     private ProgressBar mProgressWheel;
 
     public Bitmap mBitmap = null;
-    
+
     private static final String TAG = PreviewImageFragment.class.getSimpleName();
 
     private boolean mIgnoreFirstSavedState;
-    
+
     private LoadBitmapTask mLoadBitmapTask = null;
 
 
@@ -106,7 +106,7 @@ public class PreviewImageFragment extends FileFragment {
         return frag;
     }
 
-    
+
     
     /**
      *  Creates an empty fragment for image previews.
@@ -120,8 +120,8 @@ public class PreviewImageFragment extends FileFragment {
     public PreviewImageFragment() {
         mIgnoreFirstSavedState = false;
     }
-    
-    
+
+
     /**
      * {@inheritDoc}
      */
@@ -136,14 +136,14 @@ public class PreviewImageFragment extends FileFragment {
         mIgnoreFirstSavedState = args.getBoolean(ARG_IGNORE_FIRST);
         setHasOptionsMenu(true);
     }
-    
+
 
     /**
      * {@inheritDoc}
      */
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
+                             Bundle savedInstanceState) {
         super.onCreateView(inflater, container, savedInstanceState);
         View view = inflater.inflate(R.layout.preview_image_fragment, container, false);
         mImageView = (TouchImageViewCustom) view.findViewById(R.id.image);
@@ -183,7 +183,7 @@ public class PreviewImageFragment extends FileFragment {
             throw new IllegalStateException("There is no local file to preview");
         }
     }
-        
+
 
     /**
      * {@inheritDoc}
@@ -193,7 +193,7 @@ public class PreviewImageFragment extends FileFragment {
         super.onSaveInstanceState(outState);
         outState.putParcelable(PreviewImageFragment.EXTRA_FILE, getFile());
     }
-    
+
 
     @Override
     public void onStart() {
@@ -205,8 +205,8 @@ public class PreviewImageFragment extends FileFragment {
             mLoadBitmapTask.execute(getFile());
         }
     }
-    
-    
+
+
     @Override
     public void onStop() {
         Log_OC.d(TAG, "onStop starts");
@@ -216,7 +216,7 @@ public class PreviewImageFragment extends FileFragment {
         }
         super.onStop();
     }
-    
+
     /**
      * {@inheritDoc}
      */
@@ -232,11 +232,11 @@ public class PreviewImageFragment extends FileFragment {
     @Override
     public void onPrepareOptionsMenu(Menu menu) {
         super.onPrepareOptionsMenu(menu);
-        
+
         if (mContainerActivity.getStorageManager() != null) {
             // Update the file
             setFile(mContainerActivity.getStorageManager().getFileById(getFile().getFileId()));
-            
+
             FileMenuFilter mf = new FileMenuFilter(
                 getFile(),
                 mContainerActivity.getStorageManager().getAccount(),
@@ -245,7 +245,7 @@ public class PreviewImageFragment extends FileFragment {
             );
             mf.filter(menu);
         }
-        
+
         // additional restriction for this fragment 
         // TODO allow renaming in PreviewImageFragment
         MenuItem item = menu.findItem(R.id.action_rename_file);
@@ -253,7 +253,7 @@ public class PreviewImageFragment extends FileFragment {
             item.setVisible(false);
             item.setEnabled(false);
         }
-        
+
         // additional restriction for this fragment 
         // TODO allow refresh file in PreviewImageFragment
         item = menu.findItem(R.id.action_sync_file);
@@ -268,11 +268,17 @@ public class PreviewImageFragment extends FileFragment {
             item.setVisible(false);
             item.setEnabled(false);
         }
-        
+
+        // additional restriction for this fragment
+        item = menu.findItem(R.id.action_copy);
+        if (item != null) {
+            item.setVisible(false);
+            item.setEnabled(false);
+        }
+
     }
 
-    
-    
+
     /**
      * {@inheritDoc}
      */
@@ -320,10 +326,10 @@ public class PreviewImageFragment extends FileFragment {
                 return false;
         }
     }
-    
+
 
     private void seeDetails() {
-        mContainerActivity.showDetails(getFile());        
+        mContainerActivity.showDetails(getFile());
     }
 
 
@@ -350,7 +356,7 @@ public class PreviewImageFragment extends FileFragment {
         super.onDestroy();
     }
 
-    
+
     /**
      * Opens the previewed image with an external application.
      */
@@ -358,7 +364,7 @@ public class PreviewImageFragment extends FileFragment {
         mContainerActivity.getFileOperationsHelper().openFile(getFile());
         finish();
     }
-    
+
     
     private class LoadBitmapTask extends AsyncTask<OCFile, Void, LoadImage> {
 
@@ -378,7 +384,7 @@ public class PreviewImageFragment extends FileFragment {
          */
         private final WeakReference<TextView> mMessageViewRef;
 
-        
+
         /**
          * Weak reference to the target {@link ProgressBar} shown while the load is in progress.
          * 
@@ -387,17 +393,17 @@ public class PreviewImageFragment extends FileFragment {
          */
         private final WeakReference<ProgressBar> mProgressWheelRef;
 
-        
+
         /**
-         * Error message to show when a load fails 
+         * Error message to show when a load fails
          */
         private int mErrorMessageId;
-        
-        
+
+
         /**
          * Constructor.
-         * 
-         * @param imageView     Target {@link ImageView} where the bitmap will be loaded into.
+         *
+         * @param imageView Target {@link ImageView} where the bitmap will be loaded into.
          */
         public LoadBitmapTask(ImageViewCustom imageView, TextView messageView,
                               ProgressBar progressWheel) {
@@ -405,7 +411,7 @@ public class PreviewImageFragment extends FileFragment {
             mMessageViewRef = new WeakReference<TextView>(messageView);
             mProgressWheelRef = new WeakReference<ProgressBar>(progressWheel);
         }
-        
+
         @Override
         protected LoadImage doInBackground(OCFile... params) {
             Bitmap result = null;
@@ -456,17 +462,18 @@ public class PreviewImageFragment extends FileFragment {
 
             } catch (NoSuchFieldError e) {
                 mErrorMessageId = R.string.common_error_unknown;
-                Log_OC.e(TAG, "Error from access to unexisting field despite protection; file " 
-                                + storagePath, e);
-                    
+                Log_OC.e(TAG, "Error from access to unexisting field despite protection; file "
+                        + storagePath, e);
+
             } catch (Throwable t) {
                 mErrorMessageId = R.string.common_error_unknown;
                 Log_OC.e(TAG, "Unexpected error loading " + getFile().getStoragePath(), t);
-                
+
             }
+
             return new LoadImage(result, ocFile);
         }
-        
+
         @Override
         protected void onCancelled(LoadImage result) {
             if (result.bitmap != null) {
@@ -479,7 +486,8 @@ public class PreviewImageFragment extends FileFragment {
             hideProgressWheel();
             if (result.bitmap != null) {
                 showLoadedImage(result);
-            } else {
+            }
+            else {
                 showErrorMessage();
             }
             if (result.bitmap != null && mBitmap != result.bitmap)  {
@@ -487,7 +495,7 @@ public class PreviewImageFragment extends FileFragment {
                 result.bitmap.recycle();
             }
         }
-        
+
         @SuppressLint("InlinedApi")
         private void showLoadedImage(LoadImage result) {
             final ImageViewCustom imageView = mImageViewRef.get();
@@ -511,7 +519,7 @@ public class PreviewImageFragment extends FileFragment {
                 messageView.setVisibility(View.GONE);
             } // else , silently finish, the fragment was destroyed
         }
-        
+
         private void showErrorMessage() {
             final ImageView imageView = mImageViewRef.get();
             if (imageView != null) {
@@ -525,14 +533,14 @@ public class PreviewImageFragment extends FileFragment {
                 messageView.setVisibility(View.VISIBLE);
             } // else , silently finish, the fragment was destroyed
         }
-        
+
         private void hideProgressWheel() {
             final ProgressBar progressWheel = mProgressWheelRef.get();
             if (progressWheel != null) {
                 progressWheel.setVisibility(View.GONE);
             }
         }
-        
+
     }
 
     /**
@@ -546,7 +554,7 @@ public class PreviewImageFragment extends FileFragment {
         return (file != null && file.isImage());
     }
 
-    
+
     /**
      * Finishes the preview
      */
@@ -554,7 +562,7 @@ public class PreviewImageFragment extends FileFragment {
         Activity container = getActivity();
         container.finish();
     }
-    
+
     public TouchImageViewCustom getImageView() {
         return mImageView;
     }

+ 147 - 128
src/com/owncloud/android/ui/preview/PreviewMediaFragment.java

@@ -49,7 +49,6 @@ import android.view.View;
 import android.view.View.OnTouchListener;
 import android.view.ViewGroup;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.Toast;
 import android.widget.VideoView;
 
@@ -68,7 +67,7 @@ import com.owncloud.android.ui.fragment.FileFragment;
 
 /**
  * This fragment shows a preview of a downloaded media file (audio or video).
- * 
+ *
  * Trying to get an instance with NULL {@link OCFile} or ownCloud {@link Account} values will
  * produce an {@link IllegalStateException}.
  * 
@@ -88,46 +87,46 @@ public class PreviewMediaFragment extends FileFragment implements
     private ImageView mImagePreview;
     private VideoView mVideoPreview;
     private int mSavedPlaybackPosition;
-    
+
     private MediaServiceBinder mMediaServiceBinder = null;
     private MediaControlView mMediaController = null;
     private MediaServiceConnection mMediaServiceConnection = null;
     private VideoHelper mVideoHelper;
     private boolean mAutoplay;
     public boolean mPrepared;
-    
+
     private static final String TAG = PreviewMediaFragment.class.getSimpleName();
 
-    
+
     /**
      * Creates a fragment to preview a file.
-     * 
+     * <p/>
      * When 'fileToDetail' or 'ocAccount' are null
-     * 
-     * @param fileToDetail      An {@link OCFile} to preview in the fragment
-     * @param ocAccount         An ownCloud account; needed to start downloads
+     *
+     * @param fileToDetail An {@link OCFile} to preview in the fragment
+     * @param ocAccount    An ownCloud account; needed to start downloads
      */
     public PreviewMediaFragment(
-            OCFile fileToDetail, 
-            Account ocAccount, 
-            int startPlaybackPosition, 
+            OCFile fileToDetail,
+            Account ocAccount,
+            int startPlaybackPosition,
             boolean autoplay) {
-        
+
         super(fileToDetail);
         mAccount = ocAccount;
         mSavedPlaybackPosition = startPlaybackPosition;
         mAutoplay = autoplay;
     }
-    
-    
+
+
     /**
-     *  Creates an empty fragment for previews.
-     * 
-     *  MUST BE KEPT: the system uses it when tries to reinstantiate a fragment automatically 
-     *  (for instance, when the device is turned a aside).
-     * 
-     *  DO NOT CALL IT: an {@link OCFile} and {@link Account} must be provided for a successful 
-     *  construction 
+     * Creates an empty fragment for previews.
+     * <p/>
+     * MUST BE KEPT: the system uses it when tries to reinstantiate a fragment automatically
+     * (for instance, when the device is turned a aside).
+     * <p/>
+     * DO NOT CALL IT: an {@link OCFile} and {@link Account} must be provided for a successful
+     * construction
      */
     public PreviewMediaFragment() {
         super();
@@ -135,8 +134,8 @@ public class PreviewMediaFragment extends FileFragment implements
         mSavedPlaybackPosition = 0;
         mAutoplay = true;
     }
-    
-    
+
+
     /**
      * {@inheritDoc}
      */
@@ -145,29 +144,29 @@ public class PreviewMediaFragment extends FileFragment implements
         super.onCreate(savedInstanceState);
         setHasOptionsMenu(true);
     }
-    
+
 
     /**
      * {@inheritDoc}
      */
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
+                             Bundle savedInstanceState) {
         super.onCreateView(inflater, container, savedInstanceState);
         Log_OC.e(TAG, "onCreateView");
 
-        
+
         mView = inflater.inflate(R.layout.file_preview, container, false);
-        
-        mImagePreview = (ImageView)mView.findViewById(R.id.image_preview);
-        mVideoPreview = (VideoView)mView.findViewById(R.id.video_preview);
+
+        mImagePreview = (ImageView) mView.findViewById(R.id.image_preview);
+        mVideoPreview = (VideoView) mView.findViewById(R.id.video_preview);
         mVideoPreview.setOnTouchListener(this);
-        
-        mMediaController = (MediaControlView)mView.findViewById(R.id.media_controller);
-        
+
+        mMediaController = (MediaControlView) mView.findViewById(R.id.media_controller);
+
         return mView;
     }
-    
+
 
     /**
      * {@inheritDoc}
@@ -188,29 +187,31 @@ public class PreviewMediaFragment extends FileFragment implements
             if (!file.isDown()) {
                 throw new IllegalStateException("There is no local file to preview");
             }
-            
-        } else {
-            file = (OCFile)savedInstanceState.getParcelable(PreviewMediaFragment.EXTRA_FILE);
+
+        }
+        else {
+            file = (OCFile) savedInstanceState.getParcelable(PreviewMediaFragment.EXTRA_FILE);
             setFile(file);
             mAccount = savedInstanceState.getParcelable(PreviewMediaFragment.EXTRA_ACCOUNT);
-            mSavedPlaybackPosition = 
+            mSavedPlaybackPosition =
                     savedInstanceState.getInt(PreviewMediaFragment.EXTRA_PLAY_POSITION);
             mAutoplay = savedInstanceState.getBoolean(PreviewMediaFragment.EXTRA_PLAYING);
-            
+
         }
         if (file != null && file.isDown()) {
             if (file.isVideo()) {
                 mVideoPreview.setVisibility(View.VISIBLE);
                 mImagePreview.setVisibility(View.GONE);
                 prepareVideo();
-            
-            } else {
+
+            }
+            else {
                 mVideoPreview.setVisibility(View.GONE);
                 mImagePreview.setVisibility(View.VISIBLE);
                 extractAndSetCoverArt(file);
             }
         }
-        
+
     }
 
     /**
@@ -244,24 +245,25 @@ public class PreviewMediaFragment extends FileFragment implements
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
         Log_OC.e(TAG, "onSaveInstanceState");
-        
+
         outState.putParcelable(PreviewMediaFragment.EXTRA_FILE, getFile());
         outState.putParcelable(PreviewMediaFragment.EXTRA_ACCOUNT, mAccount);
-        
+
         if (getFile().isVideo()) {
             mSavedPlaybackPosition = mVideoPreview.getCurrentPosition();
             mAutoplay = mVideoPreview.isPlaying();
-            outState.putInt(PreviewMediaFragment.EXTRA_PLAY_POSITION , mSavedPlaybackPosition);
-            outState.putBoolean(PreviewMediaFragment.EXTRA_PLAYING , mAutoplay);
-        } else {
+            outState.putInt(PreviewMediaFragment.EXTRA_PLAY_POSITION, mSavedPlaybackPosition);
+            outState.putBoolean(PreviewMediaFragment.EXTRA_PLAYING, mAutoplay);
+        }
+        else {
             outState.putInt(
-                    PreviewMediaFragment.EXTRA_PLAY_POSITION , 
+                    PreviewMediaFragment.EXTRA_PLAY_POSITION,
                     mMediaServiceBinder.getCurrentPosition());
             outState.putBoolean(
-                    PreviewMediaFragment.EXTRA_PLAYING , mMediaServiceBinder.isPlaying());
+                    PreviewMediaFragment.EXTRA_PLAYING, mMediaServiceBinder.isPlaying());
         }
     }
-    
+
 
     @Override
     public void onStart() {
@@ -270,17 +272,20 @@ public class PreviewMediaFragment extends FileFragment implements
 
         OCFile file = getFile();
         if (file != null && file.isDown()) {
-           if (file.isAudio()) {
-               bindMediaService();
-               
-           } else if (file.isVideo()) {
-               stopAudio();
-               playVideo(); 
-           }
+            if (file.isAudio()) {
+                bindMediaService();
+
+            }
+            else {
+                if (file.isVideo()) {
+                    stopAudio();
+                    playVideo();
+                }
+            }
         }
     }
-    
-    
+
+
     private void stopAudio() {
         Intent i = new Intent(getActivity(), MediaService.class);
         i.setAction(MediaService.ACTION_STOP_ALL);
@@ -304,7 +309,7 @@ public class PreviewMediaFragment extends FileFragment implements
     @Override
     public void onPrepareOptionsMenu(Menu menu) {
         super.onPrepareOptionsMenu(menu);
-        
+
         if (mContainerActivity.getStorageManager() != null) {
             FileMenuFilter mf = new FileMenuFilter(
                 getFile(),
@@ -329,9 +334,16 @@ public class PreviewMediaFragment extends FileFragment implements
             item.setVisible(false);
             item.setEnabled(false);
         }
+
+        // additional restriction for this fragment
+        item = menu.findItem(R.id.action_copy);
+        if (item != null) {
+            item.setVisible(false);
+            item.setEnabled(false);
+        }
     }
-    
-    
+
+
     /**
      * {@inheritDoc}
      */
@@ -381,26 +393,26 @@ public class PreviewMediaFragment extends FileFragment implements
                 return false;
         }
     }
-    
 
 
     /**
      * Update the file of the fragment with file value
+     *
      * @param file
      */
-    public void updateFile(OCFile file){
+    public void updateFile(OCFile file) {
         setFile(file);
     }
-    
+
     private void sendFile() {
         stopPreview(false);
         mContainerActivity.getFileOperationsHelper().sendDownloadedFile(getFile());
-        
+
     }
 
     private void seeDetails() {
         stopPreview(false);
-        mContainerActivity.showDetails(getFile());        
+        mContainerActivity.showDetails(getFile());
     }
 
 
@@ -411,77 +423,80 @@ public class PreviewMediaFragment extends FileFragment implements
         mVideoPreview.setOnCompletionListener(mVideoHelper);
         mVideoPreview.setOnErrorListener(mVideoHelper);
     }
-    
+
     @SuppressWarnings("static-access")
     private void playVideo() {
         // create and prepare control panel for the user
         mMediaController.setMediaPlayer(mVideoPreview);
-        
+
         // load the video file in the video player ; 
         // when done, VideoHelper#onPrepared() will be called
         Uri uri = Uri.parse(getFile().getStoragePath());
         mVideoPreview.setVideoPath(uri.encode(getFile().getStoragePath()));
     }
-    
+
 
     private class VideoHelper implements OnCompletionListener, OnPreparedListener, OnErrorListener {
-        
-        /** 
+
+        /**
          * Called when the file is ready to be played.
-         * 
+         * <p/>
          * Just starts the playback.
-         * 
+         *
          * @param   vp    {@link MediaPlayer} instance performing the playback.
          */
         @Override
         public void onPrepared(MediaPlayer vp) {
             Log_OC.e(TAG, "onPrepared");
             mVideoPreview.seekTo(mSavedPlaybackPosition);
-            if (mAutoplay) { 
+            if (mAutoplay) {
                 mVideoPreview.start();
             }
             mMediaController.setEnabled(true);
             mMediaController.updatePausePlay();
             mPrepared = true;
         }
-        
-        
+
+
         /**
          * Called when the file is finished playing.
-         *  
+         * <p/>
          * Finishes the activity.
-         * 
-         * @param   mp    {@link MediaPlayer} instance performing the playback.
+         *
+         * @param mp {@link MediaPlayer} instance performing the playback.
          */
         @Override
-        public void onCompletion(MediaPlayer  mp) {
+        public void onCompletion(MediaPlayer mp) {
             Log_OC.e(TAG, "completed");
             if (mp != null) {
                 mVideoPreview.seekTo(0);
                 // next lines are necessary to work around undesired video loops
                 if (Build.VERSION.SDK_INT == Build.VERSION_CODES.GINGERBREAD) {
-                    mVideoPreview.pause();   
-                    
-                } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.GINGERBREAD_MR1) {
-                    // mVideePreview.pause() is not enough
-                    
-                    mMediaController.setEnabled(false);
-                    mVideoPreview.stopPlayback();
-                    mAutoplay = false;
-                    mSavedPlaybackPosition = 0;
-                    mVideoPreview.setVideoPath(getFile().getStoragePath());
+                    mVideoPreview.pause();
+
+                }
+                else {
+                    if (Build.VERSION.SDK_INT == Build.VERSION_CODES.GINGERBREAD_MR1) {
+                        // mVideePreview.pause() is not enough
+
+                        mMediaController.setEnabled(false);
+                        mVideoPreview.stopPlayback();
+                        mAutoplay = false;
+                        mSavedPlaybackPosition = 0;
+                        mVideoPreview.setVideoPath(getFile().getStoragePath());
+                    }
                 }
             } // else : called from onError()
             mMediaController.updatePausePlay();
         }
-        
-        
+
+
         /**
          * Called when an error in playback occurs.
-         * 
-         * @param   mp      {@link MediaPlayer} instance performing the playback.
-         * @param   what    Type of error
-         * @param   extra   Extra code specific to the error
+         *
+         * @param mp    {@link MediaPlayer} instance performing the playback.
+         * @param what  Type of error
+         * @param extra Extra code specific to the error
          */
         @Override
         public boolean onError(MediaPlayer mp, int what, int extra) {
@@ -502,28 +517,28 @@ public class PreviewMediaFragment extends FileFragment implements
             }
             return true;
         }
-        
+
     }
 
-    
+
     @Override
     public void onPause() {
         Log_OC.e(TAG, "onPause");
         super.onPause();
     }
-    
+
     @Override
     public void onResume() {
         super.onResume();
         Log_OC.e(TAG, "onResume");
     }
-    
+
     @Override
     public void onDestroy() {
         Log_OC.e(TAG, "onDestroy");
         super.onDestroy();
     }
-    
+
     @Override
     public void onStop() {
         Log_OC.e(TAG, "onStop");
@@ -538,10 +553,10 @@ public class PreviewMediaFragment extends FileFragment implements
             mMediaServiceConnection = null;
             mMediaServiceBinder = null;
         }
-        
+
         super.onStop();
     }
-    
+
     @Override
     public boolean onTouch(View v, MotionEvent event) {
         if (event.getAction() == MotionEvent.ACTION_DOWN && v == mVideoPreview) {
@@ -554,7 +569,7 @@ public class PreviewMediaFragment extends FileFragment implements
         return false;
     }
 
-    
+
     private void startFullScreenVideo() {
         Intent i = new Intent(getActivity(), PreviewVideoActivity.class);
         i.putExtra(FileActivity.EXTRA_ACCOUNT, mAccount);
@@ -566,29 +581,30 @@ public class PreviewMediaFragment extends FileFragment implements
     }
 
     @Override
-    public void onConfigurationChanged (Configuration newConfig) {
+    public void onConfigurationChanged(Configuration newConfig) {
         Log_OC.e(TAG, "onConfigurationChanged " + this);
     }
-    
+
     @Override
-    public void onActivityResult (int requestCode, int resultCode, Intent data) {
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
         Log_OC.e(TAG, "onActivityResult " + this);
         super.onActivityResult(requestCode, resultCode, data);
         if (resultCode == Activity.RESULT_OK) {
             mSavedPlaybackPosition = data.getExtras().getInt(
                     PreviewVideoActivity.EXTRA_START_POSITION);
-            mAutoplay = data.getExtras().getBoolean(PreviewVideoActivity.EXTRA_AUTOPLAY); 
+            mAutoplay = data.getExtras().getBoolean(PreviewVideoActivity.EXTRA_AUTOPLAY);
         }
     }
-    
+
 
     private void playAudio() {
         OCFile file = getFile();
         if (!mMediaServiceBinder.isPlaying(file)) {
             Log_OC.d(TAG, "starting playback of " + file.getStoragePath());
             mMediaServiceBinder.start(mAccount, file, mAutoplay, mSavedPlaybackPosition);
-            
-        } else {
+
+        }
+        else {
             if (!mMediaServiceBinder.isPlaying() && mAutoplay) {
                 mMediaServiceBinder.start();
                 mMediaController.updatePausePlay();
@@ -625,7 +641,8 @@ public class PreviewMediaFragment extends FileFragment implements
 
                         Log_OC.d(TAG, "Successfully bound to MediaService, MediaController ready");
 
-                    } else {
+                    }
+                    else {
                         Log_OC.e(TAG, "Unexpected response from MediaService while binding");
                     }
                 }
@@ -647,7 +664,8 @@ public class PreviewMediaFragment extends FileFragment implements
                 Log_OC.e(TAG, "Media service suddenly disconnected");
                 if (mMediaController != null) {
                     mMediaController.setMediaPlayer(null);
-                } else {
+                }
+                else {
                     Toast.makeText(
                             getActivity(),
                             "No media controller to release when disconnected from media service", 
@@ -657,9 +675,8 @@ public class PreviewMediaFragment extends FileFragment implements
                 mMediaServiceConnection = null;
             }
         }
-    }    
+    }
 
-    
 
     /**
      * Opens the previewed file with an external application.
@@ -669,31 +686,33 @@ public class PreviewMediaFragment extends FileFragment implements
         mContainerActivity.getFileOperationsHelper().openFile(getFile());
         finish();
     }
-    
+
     /**
      * Helper method to test if an {@link OCFile} can be passed to a {@link PreviewMediaFragment}
-     *  to be previewed.
-     * 
-     * @param file      File to test if can be previewed.
-     * @return          'True' if the file can be handled by the fragment.
+     * to be previewed.
+     *
+     * @param file File to test if can be previewed.
+     * @return 'True' if the file can be handled by the fragment.
      */
     public static boolean canBePreviewed(OCFile file) {
         return (file != null && (file.isAudio() || file.isVideo()));
     }
-    
+
 
     public void stopPreview(boolean stopAudio) {
         OCFile file = getFile();
         if (file.isAudio() && stopAudio) {
             mMediaServiceBinder.pause();
-            
-        } else if (file.isVideo()) {
-            mVideoPreview.stopPlayback();
+
+        }
+        else {
+            if (file.isVideo()) {
+                mVideoPreview.stopPlayback();
+            }
         }
     }
 
 
-
     /**
      * Finishes the preview
      */
@@ -709,12 +728,12 @@ public class PreviewMediaFragment extends FileFragment implements
         Log_OC.e(TAG, "getting position: " + mSavedPlaybackPosition);
         return mSavedPlaybackPosition;
     }
-    
+
     public boolean isPlaying() {
         if (mPrepared) {
             mAutoplay = mVideoPreview.isPlaying();
         }
         return mAutoplay;
     }
-    
+
 }

+ 66 - 46
src/com/owncloud/android/utils/ErrorMessageAdapter.java

@@ -21,17 +21,13 @@
 
 package com.owncloud.android.utils;
 
-import java.io.File;
-import java.net.SocketTimeoutException;
-
-import org.apache.commons.httpclient.ConnectTimeoutException;
-
 import android.content.res.Resources;
 
 import com.owncloud.android.R;
 import com.owncloud.android.lib.common.operations.RemoteOperation;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.operations.CopyFileOperation;
 import com.owncloud.android.operations.CreateFolderOperation;
 import com.owncloud.android.operations.CreateShareOperation;
 import com.owncloud.android.operations.DownloadFileOperation;
@@ -43,6 +39,11 @@ import com.owncloud.android.operations.SynchronizeFolderOperation;
 import com.owncloud.android.operations.UnshareLinkOperation;
 import com.owncloud.android.operations.UploadFileOperation;
 
+import org.apache.commons.httpclient.ConnectTimeoutException;
+
+import java.io.File;
+import java.net.SocketTimeoutException;
+
 /**
  * Class to choose proper error messages to show to the user depending on the results of operations,
  * always following the same policy
@@ -51,16 +52,16 @@ import com.owncloud.android.operations.UploadFileOperation;
 public class ErrorMessageAdapter {
 
     public ErrorMessageAdapter() {
-        
+
     }
 
     public static String getErrorCauseMessage(RemoteOperationResult result,
                                               RemoteOperation operation, Resources res) {
         
         String message = null;
-        
+
         if (operation instanceof UploadFileOperation) {
-            
+
             if (result.isSuccess()) {
                 message = String.format(
                         res.getString(R.string.uploader_upload_succeeded_content_single),
@@ -76,7 +77,7 @@ public class ErrorMessageAdapter {
                 } else if (result.getCode() == ResultCode.QUOTA_EXCEEDED) {
                     message = res.getString(R.string.failed_upload_quota_exceeded_text);
                     */
-                    
+
                 } else if (result.getCode() == ResultCode.FORBIDDEN) {
                     message = String.format(res.getString(R.string.forbidden_permissions),
                             res.getString(R.string.uploader_upload_forbidden_permissions));
@@ -90,14 +91,14 @@ public class ErrorMessageAdapter {
                             ((UploadFileOperation) operation).getFileName());
                 }
             }
-            
+
         } else if (operation instanceof DownloadFileOperation) {
-            
+
             if (result.isSuccess()) {
                 message = String.format(
                         res.getString(R.string.downloader_download_succeeded_content),
                         new File(((DownloadFileOperation) operation).getSavePath()).getName());
-                
+
             } else {
                 if (result.getCode() == ResultCode.FILE_NOT_FOUND) {
                     message = res.getString(R.string.downloader_download_file_not_found);
@@ -108,11 +109,11 @@ public class ErrorMessageAdapter {
                             ((DownloadFileOperation) operation).getSavePath()).getName());
                 }
             }
-            
+
         } else if (operation instanceof RemoveFileOperation) {
             if (result.isSuccess()) {
                 message = res.getString(R.string.remove_success_msg);
-                
+
             } else {
                 if (result.getCode().equals(ResultCode.FORBIDDEN)) {
                     // Error --> No permissions
@@ -120,7 +121,7 @@ public class ErrorMessageAdapter {
                             res.getString(R.string.forbidden_permissions_delete));
                 } else if (isNetworkError(result.getCode())) {
                     message = getErrorMessage(result, res);
-                    
+
                 } else {
                     message = res.getString(R.string.remove_fail_msg);
                 }
@@ -140,19 +141,19 @@ public class ErrorMessageAdapter {
 
             } else if (isNetworkError(result.getCode())) {
                 message = getErrorMessage(result, res);
-                
+
             } else if (result.getCode() == ResultCode.INVALID_CHARACTER_DETECT_IN_SERVER) {
                 message = res.getString(R.string.filename_forbidden_charaters_from_server);
 
             } else {
-                message = res.getString(R.string.rename_server_fail_msg); 
+                message = res.getString(R.string.rename_server_fail_msg);
             }
-            
+
         } else if (operation instanceof SynchronizeFileOperation) {
             if (!((SynchronizeFileOperation) operation).transferWasRequested()) {
                 message = res.getString(R.string.sync_file_nothing_to_do_msg);
             }
-            
+
         } else if (operation instanceof CreateFolderOperation) {
             if (result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME) {
                 message = res.getString(R.string.filename_forbidden_characters);
@@ -163,7 +164,7 @@ public class ErrorMessageAdapter {
 
             } else if (isNetworkError(result.getCode())) {
                 message = getErrorMessage(result, res);
-                
+
             } else if (result.getCode() == ResultCode.INVALID_CHARACTER_DETECT_IN_SERVER) {
                 message = res.getString(R.string.filename_forbidden_charaters_from_server);
             } else {
@@ -172,7 +173,7 @@ public class ErrorMessageAdapter {
         } else if (operation instanceof CreateShareOperation) {        
             if (result.getCode() == ResultCode.SHARE_NOT_FOUND)  {      // Error --> SHARE_NOT_FOUND
                 message = res.getString(R.string.share_link_file_no_exist);
-                
+
             } else if (result.getCode() == ResultCode.SHARE_FORBIDDEN) {
                 // Error --> No permissions
                 message = String.format(res.getString(R.string.forbidden_permissions),
@@ -180,17 +181,17 @@ public class ErrorMessageAdapter {
 
             } else if (isNetworkError(result.getCode())) {
                 message = getErrorMessage(result, res);
-                
+
             } else {    // Generic error
                 // Show a Message, operation finished without success
                 message = res.getString(R.string.share_link_file_error);
             }
-            
+
         } else if (operation instanceof UnshareLinkOperation) {
-        
+
             if (result.getCode() == ResultCode.SHARE_NOT_FOUND)  {      // Error --> SHARE_NOT_FOUND
                 message = res.getString(R.string.unshare_link_file_no_exist);
-                
+
             } else if (result.getCode() == ResultCode.SHARE_FORBIDDEN) {
                 // Error --> No permissions
                 message = String.format(res.getString(R.string.forbidden_permissions),
@@ -198,17 +199,18 @@ public class ErrorMessageAdapter {
 
             } else if (isNetworkError(result.getCode())) {
                 message = getErrorMessage(result, res);
-                
+
             } else {    // Generic error
                 // Show a Message, operation finished without success
                 message = res.getString(R.string.unshare_link_file_error);
             }
         } else if (operation instanceof MoveFileOperation) {
 
-            if (result.getCode() == ResultCode.FILE_NOT_FOUND) {
+            if(isNetworkError(result.getCode())){
+                message = getErrorMessage(result, res);
+            } else if (result.getCode() == ResultCode.FILE_NOT_FOUND) {
                 message = res.getString(R.string.move_file_not_found);
-                
-            } else if (result.getCode() == ResultCode.INVALID_MOVE_INTO_DESCENDANT)  {
+            } else if (result.getCode() == ResultCode.INVALID_MOVE_INTO_DESCENDANT) {
                 message = res.getString(R.string.move_file_invalid_into_descendent);
 
             } else if (result.getCode() == ResultCode.INVALID_OVERWRITE) {
@@ -240,44 +242,62 @@ public class ErrorMessageAdapter {
                             folderPathName);
                 }
             }
+        } else if (operation instanceof CopyFileOperation) {
+            if(isNetworkError(result.getCode())){
+                message = getErrorMessage(result, res);
+            } else if (result.getCode() == ResultCode.FILE_NOT_FOUND) {
+                message = res.getString(R.string.copy_file_not_found);
+            } else if (result.getCode() == ResultCode.INVALID_COPY_INTO_DESCENDANT) {
+                message = res.getString(R.string.copy_file_invalid_into_descendent);
+
+            } else if (result.getCode() == ResultCode.INVALID_OVERWRITE) {
+                message = res.getString(R.string.copy_file_invalid_overwrite);
+
+            } else if (result.getCode() == ResultCode.FORBIDDEN) {
+                message = String.format(res.getString(R.string.forbidden_permissions),
+                        res.getString(R.string.forbidden_permissions_copy));
+
+            } else {    // Generic error
+                // Show a Message, operation finished without success
+                message = res.getString(R.string.copy_file_error);
+            }
         }
-        
+
         return message;
     }
-    
-    private static String getErrorMessage(RemoteOperationResult result , Resources res) {
-        
+
+    private static String getErrorMessage(RemoteOperationResult result, Resources res) {
+
         String message = null;
-        
+
         if (!result.isSuccess()) {
-            
+
             if (result.getCode() == ResultCode.WRONG_CONNECTION) {
                 message = res.getString(R.string.network_error_socket_exception);
-                
+
             } else if (result.getCode() == ResultCode.TIMEOUT) {
                 message = res.getString(R.string.network_error_socket_exception);
-                
+
                 if (result.getException() instanceof SocketTimeoutException) {
                     message = res.getString(R.string.network_error_socket_timeout_exception);
-                } else if(result.getException() instanceof ConnectTimeoutException) {
+                } else if (result.getException() instanceof ConnectTimeoutException) {
                     message = res.getString(R.string.network_error_connect_timeout_exception);
-                } 
-                
+                }
+
             } else if (result.getCode() == ResultCode.HOST_NOT_AVAILABLE) {
                 message = res.getString(R.string.network_host_not_available);
             }
         }
-        
+
         return message;
     }
-    
+
     private static boolean isNetworkError(RemoteOperationResult.ResultCode code) {
-        if (code == ResultCode.WRONG_CONNECTION || 
-                code == ResultCode.TIMEOUT || 
+        if (code == ResultCode.WRONG_CONNECTION ||
+                code == ResultCode.TIMEOUT ||
                 code == ResultCode.HOST_NOT_AVAILABLE) {
             return true;
-        }
-        else
+        } else
             return false;
     }
 }