Merge branch 'develop' into oauth_login
authorDavid A. Velasco <dvelasco@solidgear.es>
Thu, 25 Apr 2013 17:39:22 +0000 (19:39 +0200)
committerDavid A. Velasco <dvelasco@solidgear.es>
Thu, 25 Apr 2013 17:39:22 +0000 (19:39 +0200)
Conflicts:
AndroidManifest.xml
res/values/strings.xml
src/com/owncloud/android/Uploader.java
src/com/owncloud/android/authentication/AccountAuthenticator.java
src/com/owncloud/android/authenticator/AuthenticationRunnable.java
src/com/owncloud/android/authenticator/OnAuthenticationResultListener.java
src/com/owncloud/android/authenticator/OnConnectCheckListener.java
src/com/owncloud/android/files/InstantUploadBroadcastReceiver.java
src/com/owncloud/android/files/services/FileDownloader.java
src/com/owncloud/android/files/services/FileOperation.java
src/com/owncloud/android/files/services/FileUploader.java
src/com/owncloud/android/network/OwnCloudClientUtils.java
src/com/owncloud/android/operations/RemoteOperationResult.java
src/com/owncloud/android/operations/UpdateOCVersionOperation.java
src/com/owncloud/android/syncadapter/FileSyncAdapter.java
src/com/owncloud/android/ui/activity/AccountSelectActivity.java
src/com/owncloud/android/ui/activity/AuthenticatorActivity.java
src/com/owncloud/android/ui/activity/FileDisplayActivity.java
src/com/owncloud/android/ui/fragment/FileDetailFragment.java
src/eu/alefzero/webdav/WebdavClient.java

217 files changed:
AndroidManifest.xml
CONTRIBUTING.md [new file with mode: 0644]
LICENSE.txt
README.md [new file with mode: 0644]
README.setup [deleted file]
SETUP.md [new file with mode: 0644]
THIRD_PARTY.txt [new file with mode: 0644]
doc/CodeStyleFormatter.xml [new file with mode: 0644]
issue_template.md [new file with mode: 0644]
oc_jb_workaround/src/com/owncloud/android/workaround/accounts/AccountAuthenticatorService.java
pom.xml [new file with mode: 0644]
res/anim/disappear.xml
res/anim/grow_from_bottom.xml
res/anim/grow_from_bottomleft_to_topright.xml
res/anim/grow_from_bottomright_to_topleft.xml
res/anim/grow_from_top.xml
res/anim/grow_from_topleft_to_bottomright.xml
res/anim/grow_from_topright_to_bottomleft.xml
res/anim/pump_bottom.xml
res/anim/pump_top.xml
res/anim/shrink_from_bottom.xml
res/anim/shrink_from_bottomleft_to_topright.xml
res/anim/shrink_from_bottomright_to_topleft.xml
res/anim/shrink_from_top.xml
res/anim/shrink_from_topleft_to_bottomright.xml
res/anim/shrink_from_topright_to_bottomleft.xml
res/drawable-hdpi/image_fail.png [new file with mode: 0644]
res/drawable-ldpi/image_fail.png [new file with mode: 0644]
res/drawable-mdpi/image_fail.png [new file with mode: 0644]
res/drawable/action_item_btn.xml
res/drawable/btn.xml
res/drawable/btn_round.xml
res/drawable/btn_round_pressed.xml
res/drawable/connection_secure.xml
res/drawable/list_selector.xml
res/drawable/main_header_bg.xml
res/drawable/progress_small.xml
res/drawable/split_action_bg.xml
res/drawable/uploader_list_separator.xml
res/layout-land/account_setup.xml
res/layout-large-land/files.xml
res/layout-v14/generic_explanation.xml
res/layout/account_setup.xml
res/layout/action_item.xml
res/layout/audio_player.xml [new file with mode: 0644]
res/layout/authenticator_getting_started_fragment.xml
res/layout/edit_box_dialog.xml
res/layout/extensions_available_dialog.xml
res/layout/failed_upload_files.xml [new file with mode: 0644]
res/layout/failed_upload_message_view.xml [new file with mode: 0644]
res/layout/file_activity_details.xml
res/layout/file_details_empty.xml
res/layout/file_details_fragment.xml
res/layout/file_display_action_list_element.xml
res/layout/file_download_fragment.xml [new file with mode: 0644]
res/layout/file_preview.xml [new file with mode: 0644]
res/layout/files.xml
res/layout/fragment_changelog.xml
res/layout/generic_explanation.xml
res/layout/landing_page_fragment.xml
res/layout/landing_page_item.xml
res/layout/list_fragment.xml
res/layout/list_item.xml
res/layout/log_item.xml [new file with mode: 0644]
res/layout/log_send_file.xml [new file with mode: 0644]
res/layout/main.xml
res/layout/media_control.xml [new file with mode: 0644]
res/layout/no_account_available.xml
res/layout/pick_account_layout.xml
res/layout/pincodelock.xml
res/layout/popup.xml
res/layout/preview_image_activity.xml [new file with mode: 0644]
res/layout/preview_image_fragment.xml [new file with mode: 0644]
res/layout/progressbar_layout.xml
res/layout/selected_account_element.xml
res/layout/ssl_validator_layout.xml
res/layout/upload_files_layout.xml
res/layout/uploader_layout.xml
res/layout/uploader_list_item_layout.xml
res/layout/video_layout.xml [new file with mode: 0644]
res/menu/account_picker.xml
res/menu/account_picker_long_click.xml
res/menu/file_actions_menu.xml [new file with mode: 0644]
res/menu/file_context_menu.xml [deleted file]
res/menu/main_menu.xml [new file with mode: 0644]
res/menu/menu.xml [deleted file]
res/menu/session_context_menu.xml
res/raw-de/changelog.html
res/raw-es/changelog.html
res/raw/changelog.html
res/values-de-rDE/strings.xml
res/values-de/strings.xml
res/values/bools.xml
res/values/colors.xml
res/values/strings.xml
res/values/styles.xml
res/values/urls.xml
res/xml/authenticator.xml
res/xml/preferences.xml
res/xml/preferences_new_session.xml
res/xml/syncadapter_files.xml
setup_env.bat [new file with mode: 0644]
src/com/owncloud/android/AccountUtils.java
src/com/owncloud/android/DisplayUtils.java
src/com/owncloud/android/Log_OC.java [new file with mode: 0644]
src/com/owncloud/android/OwnCloudSession.java
src/com/owncloud/android/Uploader.java
src/com/owncloud/android/authentication/AccountAuthenticator.java
src/com/owncloud/android/authentication/AccountAuthenticatorService.java
src/com/owncloud/android/authentication/AuthenticatorActivity.java
src/com/owncloud/android/authentication/OAuth2Constants.java
src/com/owncloud/android/datamodel/DataStorageManager.java
src/com/owncloud/android/datamodel/FileDataStorageManager.java
src/com/owncloud/android/datamodel/OCFile.java
src/com/owncloud/android/db/DbHandler.java
src/com/owncloud/android/db/ProviderMeta.java
src/com/owncloud/android/extensions/ExtensionsAvailableActivity.java
src/com/owncloud/android/extensions/ExtensionsAvailableDialog.java
src/com/owncloud/android/extensions/ExtensionsListActivity.java
src/com/owncloud/android/files/BootupBroadcastReceiver.java
src/com/owncloud/android/files/InstantUploadBroadcastReceiver.java
src/com/owncloud/android/files/OwnCloudFileObserver.java
src/com/owncloud/android/files/managers/OCNotificationManager.java
src/com/owncloud/android/files/services/FileDownloader.java
src/com/owncloud/android/files/services/FileObserverService.java
src/com/owncloud/android/files/services/FileUploader.java
src/com/owncloud/android/files/services/OnUploadCompletedListener.java
src/com/owncloud/android/location/LocationServiceLauncherReciever.java
src/com/owncloud/android/location/LocationUpdateService.java
src/com/owncloud/android/media/MediaControlView.java [new file with mode: 0644]
src/com/owncloud/android/media/MediaService.java [new file with mode: 0644]
src/com/owncloud/android/media/MediaServiceBinder.java [new file with mode: 0644]
src/com/owncloud/android/network/AdvancedSslSocketFactory.java
src/com/owncloud/android/network/AdvancedX509TrustManager.java
src/com/owncloud/android/network/BearerAuthScheme.java
src/com/owncloud/android/network/BearerCredentials.java
src/com/owncloud/android/network/CertificateCombinedException.java
src/com/owncloud/android/network/OwnCloudClientUtils.java
src/com/owncloud/android/network/ProgressiveDataTransferer.java [new file with mode: 0644]
src/com/owncloud/android/operations/ChunkedUploadFileOperation.java
src/com/owncloud/android/operations/CreateFolderOperation.java
src/com/owncloud/android/operations/DownloadFileOperation.java
src/com/owncloud/android/operations/ExistenceCheckOperation.java
src/com/owncloud/android/operations/OAuth2GetAccessToken.java
src/com/owncloud/android/operations/OnRemoteOperationListener.java
src/com/owncloud/android/operations/OperationCancelledException.java
src/com/owncloud/android/operations/OwnCloudServerCheckOperation.java
src/com/owncloud/android/operations/RemoteOperation.java
src/com/owncloud/android/operations/RemoteOperationResult.java
src/com/owncloud/android/operations/RemoveFileOperation.java
src/com/owncloud/android/operations/RenameFileOperation.java
src/com/owncloud/android/operations/SynchronizeFileOperation.java
src/com/owncloud/android/operations/SynchronizeFolderOperation.java
src/com/owncloud/android/operations/UpdateOCVersionOperation.java
src/com/owncloud/android/operations/UploadFileOperation.java
src/com/owncloud/android/providers/FileContentProvider.java
src/com/owncloud/android/syncadapter/AbstractOwnCloudSyncAdapter.java
src/com/owncloud/android/syncadapter/ContactSyncAdapter.java
src/com/owncloud/android/syncadapter/ContactSyncService.java
src/com/owncloud/android/syncadapter/FileSyncAdapter.java
src/com/owncloud/android/syncadapter/FileSyncService.java
src/com/owncloud/android/ui/ActionItem.java
src/com/owncloud/android/ui/CustomPopup.java
src/com/owncloud/android/ui/ExtendedListView.java
src/com/owncloud/android/ui/FragmentListView.java
src/com/owncloud/android/ui/QuickAction.java
src/com/owncloud/android/ui/activity/AccountSelectActivity.java
src/com/owncloud/android/ui/activity/ConflictsResolveActivity.java
src/com/owncloud/android/ui/activity/ErrorsWhileCopyingHandlerActivity.java
src/com/owncloud/android/ui/activity/FailedUploadActivity.java [new file with mode: 0644]
src/com/owncloud/android/ui/activity/FileDetailActivity.java
src/com/owncloud/android/ui/activity/FileDisplayActivity.java
src/com/owncloud/android/ui/activity/GenericExplanationActivity.java
src/com/owncloud/android/ui/activity/InstantUploadActivity.java [new file with mode: 0644]
src/com/owncloud/android/ui/activity/LandingActivity.java
src/com/owncloud/android/ui/activity/LogHistoryActivity.java [new file with mode: 0644]
src/com/owncloud/android/ui/activity/PinCodeActivity.java
src/com/owncloud/android/ui/activity/Preferences.java
src/com/owncloud/android/ui/activity/PreferencesNewSession.java
src/com/owncloud/android/ui/activity/TransferServiceGetter.java
src/com/owncloud/android/ui/activity/UploadFilesActivity.java
src/com/owncloud/android/ui/adapter/FileListActionListAdapter.java
src/com/owncloud/android/ui/adapter/FileListListAdapter.java
src/com/owncloud/android/ui/adapter/LandingScreenAdapter.java
src/com/owncloud/android/ui/adapter/LocalFileListAdapter.java
src/com/owncloud/android/ui/adapter/LogListAdapter.java [new file with mode: 0644]
src/com/owncloud/android/ui/dialog/ChangelogDialog.java
src/com/owncloud/android/ui/dialog/ConflictsResolveDialog.java
src/com/owncloud/android/ui/dialog/EditNameDialog.java
src/com/owncloud/android/ui/dialog/IndeterminateProgressDialog.java
src/com/owncloud/android/ui/dialog/SslValidatorDialog.java
src/com/owncloud/android/ui/fragment/AuthenticatorAccountDetailsFragment.java
src/com/owncloud/android/ui/fragment/AuthenticatorGetStartedFragment.java
src/com/owncloud/android/ui/fragment/ConfirmationDialogFragment.java
src/com/owncloud/android/ui/fragment/FileDetailFragment.java
src/com/owncloud/android/ui/fragment/FileFragment.java [new file with mode: 0644]
src/com/owncloud/android/ui/fragment/LandingPageFragment.java
src/com/owncloud/android/ui/fragment/LocalFileListFragment.java
src/com/owncloud/android/ui/fragment/OCFileListFragment.java
src/com/owncloud/android/ui/preview/FileDownloadFragment.java [new file with mode: 0644]
src/com/owncloud/android/ui/preview/PreviewImageActivity.java [new file with mode: 0644]
src/com/owncloud/android/ui/preview/PreviewImageFragment.java [new file with mode: 0644]
src/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java [new file with mode: 0644]
src/com/owncloud/android/ui/preview/PreviewMediaFragment.java [new file with mode: 0644]
src/com/owncloud/android/ui/preview/PreviewVideoActivity.java [new file with mode: 0644]
src/com/owncloud/android/utils/FileStorageUtils.java
src/com/owncloud/android/utils/OwnCloudVersion.java
src/com/owncloud/android/utils/RecursiveFileObserver.java
src/com/owncloud/android/widgets/ActionEditText.java
src/eu/alefzero/webdav/ChunkFromFileChannelRequestEntity.java
src/eu/alefzero/webdav/FileRequestEntity.java
src/eu/alefzero/webdav/OnDatatransferProgressListener.java
src/eu/alefzero/webdav/WebdavClient.java
src/eu/alefzero/webdav/WebdavEntry.java
src/eu/alefzero/webdav/WebdavUtils.java
tests/src/com/owncloud/android/test/AccountUtilsTest.java
third_party/transifex-client/LICENSE

index 745d09f..ba32f6c 100644 (file)
-<?xml version="1.0" encoding="utf-8"?>\r
-<!-- \r
-  ownCloud Android client application\r
-\r
-  Copyright (C) 2012  Bartek Przybylski\r
-  Copyright (C) 2012-2013 ownCloud Inc.\r
-\r
-  This program is free software: you can redistribute it and/or modify\r
-  it under the terms of the GNU General Public License as published by\r
-  the Free Software Foundation, either version 2 of the License, or\r
-  (at your option) any later version.\r
-\r
-  This program is distributed in the hope that it will be useful,\r
-  but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
-  GNU General Public License for more details.\r
-\r
-  You should have received a copy of the GNU General Public License\r
-  along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
- -->\r
-<manifest package="com.owncloud.android"\r
-    android:versionCode="103022"\r
-    android:versionName="1.3.22" xmlns:android="http://schemas.android.com/apk/res/android">\r
-\r
-    <uses-permission android:name="android.permission.GET_ACCOUNTS" />\r
-    <uses-permission android:name="android.permission.USE_CREDENTIALS" />\r
-    <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />\r
-    <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />\r
-    <uses-permission android:name="android.permission.INTERNET" />\r
-    <uses-permission android:name="android.permission.WRITE_SETTINGS" />\r
-    <uses-permission android:name="android.permission.READ_SYNC_STATS" />\r
-    <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />\r
-    <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />\r
-    <uses-permission android:name="android.permission.BROADCAST_STICKY" />\r
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />\r
-    <uses-permission android:name="android.permission.READ_PHONE_STATE" />\r
-    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>\r
-\r
-    <uses-sdk\r
-        android:minSdkVersion="8"\r
-        android:targetSdkVersion="13" />\r
-\r
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" >\r
-    </uses-permission>\r
-\r
-    <application\r
-        android:icon="@drawable/icon"\r
-        android:label="@string/app_name"\r
-        android:theme="@style/Theme.ownCloud"> \r
-        <activity\r
-            android:name=".ui.activity.FileDisplayActivity"\r
-            android:label="@string/app_name">\r
-            <intent-filter>\r
-                <action android:name="android.intent.action.MAIN" />\r
-\r
-                <category android:name="android.intent.category.LAUNCHER" />\r
-            </intent-filter>\r
-        </activity>\r
-        <activity android:name=".ui.activity.UploadFilesActivity">\r
-        </activity>\r
-        <activity android:name=".Uploader" >\r
-            <intent-filter>\r
-                <action android:name="android.intent.action.SEND" >\r
-                </action>\r
-\r
-                <category android:name="android.intent.category.DEFAULT" >\r
-                </category>\r
-\r
-                <data android:mimeType="*/*" >\r
-                </data>\r
-            </intent-filter>\r
-            <intent-filter>\r
-                <action android:name="android.intent.action.SEND_MULTIPLE" >\r
-                </action>\r
-\r
-                <category android:name="android.intent.category.DEFAULT" >\r
-                </category>\r
-\r
-                <data android:mimeType="*/*" >\r
-                </data>\r
-            </intent-filter>\r
-        </activity>\r
-        <activity\r
-            android:name=".ui.activity.Preferences"\r
-            android:theme="@style/Theme.ownCloud" >\r
-        </activity>\r
-        <activity android:name=".ui.activity.PreferencesNewSessionewSession" >\r
-        </activity>\r
-\r
-        <service\r
-            android:name=".authentication.AccountAuthenticatorService"\r
-            android:exported="true">\r
-            <intent-filter  android:priority="100">\r
-                <action android:name="android.accounts.AccountAuthenticator" />\r
-            </intent-filter>\r
-\r
-            <meta-data\r
-                android:name="android.accounts.AccountAuthenticator"\r
-                android:resource="@xml/authenticator" />\r
-        </service>\r
-        <service\r
-            android:name=".syncadapter.FileSyncService"\r
-            android:exported="true" >\r
-            <intent-filter>\r
-                <action android:name="android.content.SyncAdapter" />\r
-            </intent-filter>\r
-\r
-            <meta-data\r
-                android:name="android.content.SyncAdapter"\r
-                android:resource="@xml/syncadapter_files" />\r
-        </service>\r
-\r
-        <provider\r
-            android:name=".providers.FileContentProvider"\r
-            android:authorities="org.owncloud"\r
-            android:enabled="true"\r
-            android:exported="false"\r
-            android:label="@string/sync_string_files"\r
-            android:syncable="true" >\r
-        </provider>\r
-\r
-        <activity\r
-            android:name=".authentication.AuthenticatorActivity"\r
-            android:exported="true"\r
-            android:theme="@style/Theme.ownCloud.noActionBar" \r
-            android:launchMode="singleTask">\r
-            <intent-filter>\r
-                <action android:name="android.intent.action.VIEW" />\r
-                <category android:name="android.intent.category.DEFAULT" />\r
-                <category android:name="android.intent.category.BROWSABLE" />\r
-                <data android:scheme="@string/oauth2_redirect_scheme" />\r
-            </intent-filter>\r
-            <intent-filter>\r
-                <action android:name="com.owncloud.android.workaround.accounts.CREATE" />\r
-                <category android:name="android.intent.category.DEFAULT" />\r
-            </intent-filter>\r
-        </activity>\r
-\r
-        <service android:name=".files.services.FileDownloader" >\r
-        </service>\r
-\r
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 
+  ownCloud Android client application
+
+  Copyright (C) 2012  Bartek Przybylski
+  Copyright (C) 2012-2013 ownCloud Inc.
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License 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/>.
+ -->
+<manifest package="com.owncloud.android"
+    android:versionCode="104000"
+    android:versionName="1.4.0" xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
+    <uses-permission android:name="android.permission.USE_CREDENTIALS" />
+    <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
+    <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+    <uses-permission android:name="android.permission.READ_SYNC_STATS" />
+    <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
+    <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
+    <uses-permission android:name="android.permission.BROADCAST_STICKY" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
+    <uses-permission android:name="android.permission.WAKE_LOCK"/>
+    
+    <uses-sdk
+        android:minSdkVersion="8"
+        android:targetSdkVersion="13" />
+
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" >
+    </uses-permission>
+
+    <application
+        android:icon="@drawable/icon"
+        android:label="@string/app_name"
+        android:theme="@style/Theme.ownCloud"> 
+        <activity
+            android:name=".ui.activity.FileDisplayActivity"
+            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.InstantUploadActivity">
+        </activity>
+        <activity android:name=".ui.activity.FailedUploadActivity" android:theme="@android:style/Theme.Dialog" android:excludeFromRecents="true"/>
+        <activity android:name=".Uploader" >
+            <intent-filter>
+                <action android:name="android.intent.action.SEND" >
+                </action>
+
+                <category android:name="android.intent.category.DEFAULT" >
+                </category>
+
+                <data android:mimeType="*/*" >
+                </data>
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.SEND_MULTIPLE" >
+                </action>
+
+                <category android:name="android.intent.category.DEFAULT" >
+                </category>
+
+                <data android:mimeType="*/*" >
+                </data>
+            </intent-filter>
+        </activity>
+        <activity
+            android:name=".ui.activity.Preferences"
+            android:theme="@style/Theme.ownCloud" >
+        </activity>
+        <activity android:name=".ui.activity.PreferencesNewSessionewSession" >
+        </activity>
+        
+        <activity      android:name="com.owncloud.android.ui.preview.PreviewImageActivity" />
+                       
+        <activity      android:name="com.owncloud.android.ui.preview.PreviewVideoActivity"
+                                       android:label="@string/app_name"
+                                       android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
+               </activity>        
+
+        <service
+            android:name=".authentication.AccountAuthenticatorService"
+            android:exported="true">
+            <intent-filter  android:priority="100">
+                <action android:name="android.accounts.AccountAuthenticator" />
+            </intent-filter>
+
+            <meta-data
+                android:name="android.accounts.AccountAuthenticator"
+                android:resource="@xml/authenticator" />
+        </service>
+        <service
+            android:name=".syncadapter.FileSyncService"
+            android:exported="true" >
+            <intent-filter>
+                <action android:name="android.content.SyncAdapter" />
+            </intent-filter>
+
+            <meta-data
+                android:name="android.content.SyncAdapter"
+                android:resource="@xml/syncadapter_files" />
+        </service>
+
+        <provider
+            android:name=".providers.FileContentProvider"
+            android:authorities="org.owncloud"
+            android:enabled="true"
+            android:exported="false"
+            android:label="@string/sync_string_files"
+            android:syncable="true" >
+        </provider>
+
+        <activity
+            android:name=".authentication.AuthenticatorActivity"
+            android:exported="true"
+            android:theme="@style/Theme.ownCloud.noActionBar" 
+            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>
+                <action android:name="com.owncloud.android.workaround.accounts.CREATE" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+        <service android:name=".files.services.FileDownloader" />
+        <service android:name=".files.services.FileUploader" />
+        <service android:name=".media.MediaService" />
+        
         <activity android:name=".ui.activity.FileDetailActivity" />
-        <activity android:name=".ui.activity.PinCodeActivity" />\r
+        <activity android:name=".ui.activity.PinCodeActivity" />
         <activity android:name=".extensions.ExtensionsAvailableActivity"></activity>
-        <activity android:name=".extensions.ExtensionsListActivity"></activity>\r
-        <activity android:name=".ui.activity.AccountSelectActivity" android:uiOptions="none" android:label="@string/prefs_accounts"></activity>\r
+        <activity android:name=".extensions.ExtensionsListActivity"></activity>
+        <activity android:name=".ui.activity.AccountSelectActivity" android:uiOptions="none" android:label="@string/prefs_accounts"></activity>
         <activity android:name=".ui.activity.ConflictsResolveActivity"/>
-        <activity android:name=".ui.activity.GenericExplanationActivity"/>\r
-        <activity android:name=".ui.activity.ErrorsWhileCopyingHandlerActivity"/>\r
+        <activity android:name=".ui.activity.GenericExplanationActivity"/>
+        <activity android:name=".ui.activity.ErrorsWhileCopyingHandlerActivity"/>
+        
+        <activity android:name=".ui.activity.LogHistoryActivity"/>
         
-        <service android:name=".files.services.FileUploader" >\r
-        </service>
         <receiver android:name=".files.InstantUploadBroadcastReceiver">\r
             <intent-filter>\r
                 <action android:name="com.android.camera.NEW_PICTURE" />\r
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644 (file)
index 0000000..5d0c02f
--- /dev/null
@@ -0,0 +1,39 @@
+## Submitting issues
+
+If you have questions about how to use ownCloud, please direct these to the [mailing list][mailinglist] or our [forum][forum]. We are also available on [IRC][irc].
+
+### Guidelines
+* [Report the issue](https://github.com/owncloud/android/issues/new) using our [template][template], it includes all the informations we need to track down the issue.
+* This repository is *only* for issues within the ownCloud Android app code. Issues in other compontents should be reported in their own repositores: 
+  - [ownCloud code](https://github.com/owncloud/core/issues)
+  - [iOS client](https://github.com/owncloud/ios-issues/issues)
+  - [Desktop client](https://github.com/owncloud/mirall/issues)
+  - [ownCloud apps](https://github.com/owncloud/apps/issues) (e.g. Calendar, Contacts...)
+* Search the existing issues first, it's likely that your issue was already reported.
+
+If your issue appears to be a bug, and hasn't been reported, open a new issue.
+
+Help us to maximize the effort we can spend fixing issues and adding new features, by not reporting duplicate issues.
+
+[template]: https://raw.github.com/owncloud/android/master/issue_template.md
+[mailinglist]: https://mail.kde.org/mailman/listinfo/owncloud
+[forum]: http://forum.owncloud.org/
+[irc]: http://webchat.freenode.net/?channels=owncloud&uio=d4
+
+## Contributing to Source Code
+
+Thanks for wanting to contribute source code to ownCloud. That's great!
+
+Before we're able to merge your code into the ownCloud app for Android, you need to sign our [Contributor Agreement][agreement].
+
+### Guidelines
+* Contribute your code in the branch 'develop'. It will give us a better chance to test your code before merging it with stable code.
+* For your first contribution, start a pull request on develop and send us the signed [Contributor Agreement][agreement].
+* Keep on using pull requests for your next contributions although you own write permissions.
+
+[agreement]: http://owncloud.org/about/contributor-agreement/
+
+## Translations
+Please submit translations via [Transifex][transifex].
+
+[transifex]: https://www.transifex.com/projects/p/owncloud/
index d159169..4fc8809 100644 (file)
@@ -294,10 +294,9 @@ the "copyright" line and a pointer to where the full notice is found.
     Copyright (C) <year>  <name of author>
 
     This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
+    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
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..3ba9f73
--- /dev/null
+++ b/README.md
@@ -0,0 +1,6 @@
+This is the android client for [owncloud][0].
+
+Make sure you read [SETUP.md][1] when you start working on this project.
+
+[0]: https://github.com/owncloud/core
+[1]: https://raw.github.com/owncloud/android/master/SETUP.md
\ No newline at end of file
diff --git a/README.setup b/README.setup
deleted file mode 100644 (file)
index 3d592e0..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-If you want to start development of ownCloud android client you have two way to do so
-(a) building with console
-(b) building with eclipse
-
-1. Use setup_env.sh
-2a. ant clean;ant debug
-2b. Open Eclipse and import actionbarsherlock/library project to your workspace
-
-after those space you should be good to go.
-
-HAVE FUN!
-
-NOTE: You must have 'tools' and 'platforms-tools' in your path in order to run setup_env.sh 
diff --git a/SETUP.md b/SETUP.md
new file mode 100644 (file)
index 0000000..d527d4f
--- /dev/null
+++ b/SETUP.md
@@ -0,0 +1,23 @@
+  
+  If you want to start development of ownCloud android client you have two way to do so:
+
+  1. Building with console:
+
+  -  Use setup_env.sh or setup_env.bat
+  -  NOTE: You must have git, ant/bin, android/tools in your enviroment path
+  
+  2. Building with eclipse:
+
+  -  Run ant clean debug
+  -  Open Eclipse and import *actionbarsherlock/library* project to your workspace
+  -  NOTE: You must have 'tools' and 'platforms-tools' in your path in order to run setup_env.sh 
+
+        After those actions you should be good to go.
+
+        HAVE FUN!
+
+
+
+
+
+
diff --git a/THIRD_PARTY.txt b/THIRD_PARTY.txt
new file mode 100644 (file)
index 0000000..1cfdddc
--- /dev/null
@@ -0,0 +1,63 @@
+###################################################################
+  ownCloud Android client                                       
+
+  Copyright (C) 2012-2013 ownCloud Inc.
+  Copyright (C) 2012  Bartek Przybylski
+###################################################################
+
+
+###########
+# LICENSE #
+###########
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License verions 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.
+
+The source distribution of this program should include a full copy
+of the GNU GPL version 2 license in the LICENSE.txt file located 
+in its root directory. If not, see <http://www.gnu.org/licenses/>.
+
+
+########################
+# THIRD PARTY LICENSES #
+########################
+
+Both the source and binary distributions of this software contain
+some third party software. All the third party software included
+or linked is redistributed under the terms and conditions of their 
+original licenses. These licenses are compatible the GPL license 
+that govern this software, for the purposes they are being used.
+
+The third party software included and used by this project is:
+
+ * Apache JackRabbit, version 2.2.5.
+   Copyright (C) 2004-2010 The Apache Software Foundation.
+   Licensed under Apache License, Version 2.0.
+   Placed at libs/jackrabbit-webdav-2.2.5-jar-with-dependencies.jar
+   The jar file must be included in the ownCloud client APK.
+   Original license document included at libs/LICENSE.txt
+   See http://jackrabbit.apache.org/
+ * Transifex client.JavaMail API, version 1.4.3
+   Copyright (C) Transifex.
+   Licensed under GNU General Public License.
+   Placed at third_party/transifex-client.
+   Used as a helper tool, not included in the ownCloud client APK.
+   Original license document included at third_party/transifex-client/LICENSE.
+   See http://help.transifex.com/features/client/
+
+ * ActionBarSherlock, master branch.
+   Copyright (C) 2012 Jake Wharton.
+   Licensed under Apache License, Version 2.0.
+   The official repository is linked as a submodule in the 
+   ownCloud/android repository.
+   A binary JAR file must be generated from this linked project
+   and included in the ownCloud client APK.
+   See http://http://actionbarsherlock.com/
\ No newline at end of file
diff --git a/doc/CodeStyleFormatter.xml b/doc/CodeStyleFormatter.xml
new file mode 100644 (file)
index 0000000..af0006c
--- /dev/null
@@ -0,0 +1,290 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<profiles version="12">
+<profile kind="CodeFormatterProfile" name="Owncloud" version="12">
+<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="32"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
+<setting id="org.eclipse.jdt.core.compiler.source" value="1.7"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="120"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
+<setting id="org.eclipse.jdt.core.compiler.problem.assertIdentifier" value="error"/>
+<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="space"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.compiler.problem.enumIdentifier" value="error"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="2"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.7"/>
+<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode" value="enabled"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="120"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.wrap_before_binary_operator" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="2"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.7"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
+<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="49"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="2"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
+</profile>
+</profiles>
diff --git a/issue_template.md b/issue_template.md
new file mode 100644 (file)
index 0000000..493572d
--- /dev/null
@@ -0,0 +1,32 @@
+### Expected behaviour
+Tell us what should happen
+
+### Actual behaviour
+Tell us what happens instead
+
+### Steps to reproduce
+1. 
+2. 
+3. 
+
+### Environment data
+Android version:
+
+Device model: 
+
+Stock or customized system:
+
+ownCloud app version:
+
+ownCloud server version:
+
+### Logs
+#### Web server error log
+```
+Insert your webserver log here
+```
+
+#### ownCloud log (data/owncloud.log)
+```
+Insert your ownCloud log here
+```
index 102f2be..9b2922e 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
diff --git a/pom.xml b/pom.xml
new file mode 100644 (file)
index 0000000..2726333
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>com.owncloud.android</groupId>
+    <artifactId>owncloud</artifactId>
+    <version>1.3.21-SNAPSHOT</version>
+    <packaging>apk</packaging>
+    <name>Owncloud Android</name>
+
+    <properties>
+        <java-version>1.6</java-version>
+        <google.android-version>4.1.1.4</google.android-version>
+        <google.android.support-version>r7</google.android.support-version>
+        <actionbarsherlock-version>4.2.0</actionbarsherlock-version>
+    </properties>
+
+    <url>https://github.com/owncloud/android</url>
+    <description>Owncloud for Android</description>
+    <scm>
+        <connection>scm:git:git@github.com:owncloud/android.git</connection>
+        <developerConnection>scm:git:git@github.com:owncloud/android.git</developerConnection>
+        <url>https://github.com/owncloud/android</url>
+    </scm>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>com.google.android</groupId>
+            <artifactId>android</artifactId>
+            <version>${google.android-version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.android</groupId>
+            <artifactId>support-v4</artifactId>
+            <version>${google.android.support-version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.actionbarsherlock</groupId>
+            <artifactId>actionbarsherlock</artifactId>
+            <version>${actionbarsherlock-version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.actionbarsherlock</groupId>
+            <artifactId>actionbarsherlock</artifactId>
+            <version>${actionbarsherlock-version}</version>
+            <type>apklib</type>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.jackrabbit</groupId>
+            <artifactId>jackrabbit-webdav</artifactId>
+            <version>2.5.2</version>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+
+        <sourceDirectory>src</sourceDirectory>
+
+        <plugins>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.0</version>
+                <configuration>
+                    <source>${java-version}</source>
+                    <target>${java-version}</target>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>com.jayway.maven.plugins.android.generation2</groupId>
+                <artifactId>android-maven-plugin</artifactId>
+                <version>3.5.0</version>
+                <configuration>
+                    <sdk>
+                        <!-- platform or api level (api level 4 = platform 1.6)-->
+                        <path>${env.ANDROID_HOME}</path>
+                        <platform>17</platform>
+                    </sdk>
+                </configuration>
+                <extensions>true</extensions>
+            </plugin>
+
+        </plugins>
+
+    </build>
+
+</project>
+
index 1c98f62..8bb865d 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index f02d868..78bd62b 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 963cfe5..32b251b 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index d753634..f92b219 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.\r
 \r
   This program is free software: you can redistribute it and/or modify\r
-  it under the terms of the GNU General Public License as published by\r
-  the Free Software Foundation, either version 2 of the License, or\r
-  (at your option) any later version.\r
+  it under the terms of the GNU General Public License version 2,\r
+  as published by the Free Software Foundation.\r
 \r
   This program is distributed in the hope that it will be useful,\r
   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index 2dfe4db..851f847 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 8cef01b..951ca01 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 902f41e..972a5b7 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.\r
 \r
   This program is free software: you can redistribute it and/or modify\r
-  it under the terms of the GNU General Public License as published by\r
-  the Free Software Foundation, either version 2 of the License, or\r
-  (at your option) any later version.\r
+  it under the terms of the GNU General Public License version 2,\r
+  as published by the Free Software Foundation.\r
 \r
   This program is distributed in the hope that it will be useful,\r
   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index 0582789..6016e00 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 6766ea7..fa0b6c9 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 517d36c..c330980 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index c91455e..086eab3 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.\r
 \r
   This program is free software: you can redistribute it and/or modify\r
-  it under the terms of the GNU General Public License as published by\r
-  the Free Software Foundation, either version 2 of the License, or\r
-  (at your option) any later version.\r
+  it under the terms of the GNU General Public License version 2,\r
+  as published by the Free Software Foundation.\r
 \r
   This program is distributed in the hope that it will be useful,\r
   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index 7ad975b..c96ea9a 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 2c6fed0..4438ebf 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 98cc67b..680e848 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.\r
 \r
   This program is free software: you can redistribute it and/or modify\r
-  it under the terms of the GNU General Public License as published by\r
-  the Free Software Foundation, either version 2 of the License, or\r
-  (at your option) any later version.\r
+  it under the terms of the GNU General Public License version 2,\r
+  as published by the Free Software Foundation.\r
 \r
   This program is distributed in the hope that it will be useful,\r
   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index cb1d292..773b51d 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
diff --git a/res/drawable-hdpi/image_fail.png b/res/drawable-hdpi/image_fail.png
new file mode 100644 (file)
index 0000000..3866741
Binary files /dev/null and b/res/drawable-hdpi/image_fail.png differ
diff --git a/res/drawable-ldpi/image_fail.png b/res/drawable-ldpi/image_fail.png
new file mode 100644 (file)
index 0000000..77513ad
Binary files /dev/null and b/res/drawable-ldpi/image_fail.png differ
diff --git a/res/drawable-mdpi/image_fail.png b/res/drawable-mdpi/image_fail.png
new file mode 100644 (file)
index 0000000..8e650cd
Binary files /dev/null and b/res/drawable-mdpi/image_fail.png differ
index 47f73b2..dd27833 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.\r
 \r
   This program is free software: you can redistribute it and/or modify\r
-  it under the terms of the GNU General Public License as published by\r
-  the Free Software Foundation, either version 2 of the License, or\r
-  (at your option) any later version.\r
+  it under the terms of the GNU General Public License version 2,\r
+  as published by the Free Software Foundation.\r
 \r
   This program is distributed in the hope that it will be useful,\r
   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index b58eda8..0b0a399 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index a2c92aa..1a47be5 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index e567a6b..bc138c7 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 7b2944f..3e252d3 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 4af9f6b..d5881ab 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 166b666..855ba2c 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 55eb2dd..1233647 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index c0b4731..99219b6 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index f1c174f..1e53367 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index ab380ba..a420ee3 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 \r
   This program is free software: you can redistribute it and/or modify\r
-  it under the terms of the GNU General Public License as published by\r
-  the Free Software Foundation, either version 2 of the License, or\r
-  (at your option) any later version.\r
+  it under the terms of the GNU General Public License version 2,
+  as published by the Free Software Foundation.\r
 \r
   This program is distributed in the hope that it will be useful,\r
   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index 90c5c54..d874bf5 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.\r
 \r
   This program is free software: you can redistribute it and/or modify\r
-  it under the terms of the GNU General Public License as published by\r
-  the Free Software Foundation, either version 2 of the License, or\r
-  (at your option) any later version.\r
+  it under the terms of the GNU General Public License version 2,\r
+  as published by the Free Software Foundation.\r
 \r
   This program is distributed in the hope that it will be useful,\r
   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index ccc62d1..4bd3cdd 100644 (file)
@@ -5,9 +5,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index d4d3abe..ae3624c 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 \r
   This program is free software: you can redistribute it and/or modify\r
-  it under the terms of the GNU General Public License as published by\r
-  the Free Software Foundation, either version 2 of the License, or\r
-  (at your option) any later version.\r
+  it under the terms of the GNU General Public License version 2,
+  as published by the Free Software Foundation.\r
 \r
   This program is distributed in the hope that it will be useful,\r
   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index 22847de..0951958 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.\r
 \r
   This program is free software: you can redistribute it and/or modify\r
-  it under the terms of the GNU General Public License as published by\r
-  the Free Software Foundation, either version 2 of the License, or\r
-  (at your option) any later version.\r
+  it under the terms of the GNU General Public License version 2,\r
+  as published by the Free Software Foundation.\r
 \r
   This program is distributed in the hope that it will be useful,\r
   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
diff --git a/res/layout/audio_player.xml b/res/layout/audio_player.xml
new file mode 100644 (file)
index 0000000..bee0f87
--- /dev/null
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:id="@+id/main_audio_view"
+              android:layout_width="fill_parent"
+              android:layout_height="wrap_content"
+              android:layout_gravity="center"
+              android:orientation="vertical">
+  <TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center"
+    android:text="Now playing:"
+    android:textSize="25sp"
+    android:textStyle="bold"
+    />
+  <TextView
+    android:id="@+id/now_playing_text"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_marginTop="20dip"
+    android:layout_marginLeft="10dip"
+    android:layout_marginRight="10dip"
+    android:layout_gravity="center"
+    android:text="Now playing.."
+    android:textSize="16sp"
+    android:textStyle="italic"
+    />
+</LinearLayout>
\ No newline at end of file
index 3a97615..5aad778 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 7dd0b3e..500945b 100644 (file)
@@ -6,10 +6,9 @@
     Copyright (C) 2012-2013 ownCloud Inc.
 
     This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 2 of the License, or
-    (at your option) any later version.
-
+    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
index 65dd2b3..e5c3d10 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
diff --git a/res/layout/failed_upload_files.xml b/res/layout/failed_upload_files.xml
new file mode 100644 (file)
index 0000000..0f459a0
--- /dev/null
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<!-- \r
+  ownCloud Android client application\r
+\r
+  Copyright (C) 2012  Bartek Przybylski\r
+  Copyright (C) 2012-2013 ownCloud Inc.\r
+  \r
+  This program is free software: you can redistribute it and/or modify\r
+  it under the terms of the GNU General Public License version 2,\r
+  as published by the Free Software Foundation.\r
+\r
+  This program is distributed in the hope that it will be useful,\r
+  but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+  GNU General Public License for more details.\r
+\r
+  You should have received a copy of the GNU General Public License\r
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
+ -->\r
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"\r
+    android:layout_width="fill_parent"\r
+    android:layout_height="fill_parent"\r
+    android:background="@color/owncloud_white"\r
+    android:orientation="vertical"\r
+    android:id="@+id/failed_files_list_view">\r
+\r
+    <LinearLayout\r
+        android:layout_width="match_parent"\r
+        android:layout_height="wrap_content"\r
+        android:layout_gravity="right"\r
+        android:orientation="horizontal" >\r
+\r
+    </LinearLayout>\r
+\r
+    <LinearLayout\r
+        android:layout_width="match_parent"\r
+        android:layout_height="wrap_content"\r
+        android:orientation="vertical" >\r
+\r
+        <LinearLayout\r
+            android:layout_width="match_parent"\r
+            android:layout_height="wrap_content" >\r
+\r
+            <TextView\r
+                android:id="@+id/failed_upload_headline_textview"\r
+                android:layout_width="wrap_content"\r
+                android:layout_height="wrap_content"\r
+                android:layout_weight="0.93"\r
+                android:hint="@string/failed_upload_headline_hint"\r
+                android:text="@string/failed_upload_headline_text" />\r
+\r
+        </LinearLayout>\r
+\r
+        <LinearLayout\r
+            android:layout_width="match_parent"\r
+            android:layout_height="wrap_content"\r
+            android:layout_gravity="bottom|right" >\r
+\r
+            <CheckBox\r
+                android:id="@+id/failed_upload_headline_cb"\r
+                android:layout_width="wrap_content"\r
+                android:layout_height="wrap_content"\r
+                android:text="@string/failed_upload_all_cb"\r
+                android:textSize="8sp" />\r
+\r
+            <Button\r
+                android:id="@+id/failed_upload_retry_all_btn"\r
+                android:layout_width="wrap_content"\r
+                android:layout_height="wrap_content"\r
+                android:minHeight="30dp"\r
+                android:minWidth="90dp"\r
+                android:text="@string/failed_upload_headline_retryall_btn"\r
+                android:textSize="8sp" />\r
+\r
+            <Button\r
+                android:id="@+id/failed_upload_delete_all_btn"\r
+                android:layout_width="wrap_content"\r
+                android:layout_height="wrap_content"\r
+                android:minHeight="30dp"\r
+                android:minWidth="90dp"\r
+                android:text="@string/failed_upload_headline_delete_all_btn"\r
+                android:textSize="8sp" />\r
+\r
+        </LinearLayout>\r
+\r
+    </LinearLayout>\r
\r
+      <ScrollView\r
+          android:id="@+id/failedUploadScrollView"\r
+          android:layout_width="match_parent"\r
+          android:layout_height="match_parent"\r
+          android:overScrollMode="ifContentScrolls" >\r
+\r
+         <LinearLayout\r
+             android:id="@+id/failed_upload_scrollviewlayout"\r
+             android:layout_width="match_parent"\r
+             android:layout_height="wrap_content"\r
+             android:orientation="vertical">\r
+\r
+           \r
+         </LinearLayout>\r
+     </ScrollView>\r
+\r
+</LinearLayout>\r
diff --git a/res/layout/failed_upload_message_view.xml b/res/layout/failed_upload_message_view.xml
new file mode 100644 (file)
index 0000000..41e9a7d
--- /dev/null
@@ -0,0 +1,19 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"\r
+    android:id="@+id/failed_upload_message_view"\r
+    android:layout_width="fill_parent"\r
+    android:layout_height="fill_parent"\r
+    android:background="@color/owncloud_white"\r
+    android:orientation="vertical" >\r
+\r
+   <TextView android:id="@+id/faild_upload_message" \r
+       android:layout_width="match_parent"\r
+       android:layout_height="wrap_content"\r
+       android:minWidth="100dp"/>\r
+   \r
+   <Button\r
+    android:id="@+id/failed_uploadactivity_close_button"\r
+    android:layout_width="fill_parent"\r
+    android:layout_height="wrap_content"\r
+    android:text="Dismiss" />\r
+\r
+</LinearLayout>
\ No newline at end of file
index e325e61..435a248 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.\r
 \r
   This program is free software: you can redistribute it and/or modify\r
-  it under the terms of the GNU General Public License as published by\r
-  the Free Software Foundation, either version 2 of the License, or\r
-  (at your option) any later version.\r
+  it under the terms of the GNU General Public License version 2,\r
+  as published by the Free Software Foundation.\r
 \r
   This program is distributed in the hope that it will be useful,\r
   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
@@ -16,7 +15,8 @@
   GNU General Public License for more details.\r
 \r
   You should have received a copy of the GNU General Public License\r
-  along with this program.  If not, see <http://www.gnu.org/licenses/>.\r\r
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
+\r
 -->\r
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"\r
     android:layout_width="fill_parent"\r
index f264cef..3a205bd 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index dcce819..aa77c65 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 -->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
-    android:background="@color/owncloud_white" >
-
-    <ScrollView
-        android:id="@+id/fdScrollView"
-        android:layout_width="fill_parent"
-        android:layout_height="fill_parent" >
-
-        <RelativeLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content" >
-
-            <RelativeLayout
-                android:id="@+id/fdFileHeaderContainer"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginLeft="16dp"
-                android:layout_marginTop="4dp" >
-
-                <ImageView
-                    android:id="@+id/fdIcon"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:src="@drawable/file" />
-
-                <TextView
-                    android:id="@+id/fdFilename"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_centerVertical="true"
-                    android:layout_toRightOf="@+id/fdIcon"
-                    android:text="file.name"
-                    android:textAppearance="?android:attr/textAppearanceLarge" />
-            </RelativeLayout>
-
-            <RelativeLayout
-                android:id="@+id/fdDetailsContainer"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_below="@+id/fdFileHeaderContainer" >
-
-                <RelativeLayout
-                    android:id="@+id/fdLabelContainer"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_alignParentLeft="true"
-                    android:layout_alignParentTop="true"
-                    android:layout_marginLeft="16dp" >
-
-                    <TextView
-                        android:id="@+id/fdTypeLabel"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_marginTop="24dp"
-                        android:text="@string/filedetails_type"
-                        android:textAppearance="?android:attr/textAppearanceMedium" />
-
-                    <TextView
-                        android:id="@+id/fdSizeLabel"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_below="@+id/fdTypeLabel"
-                        android:layout_marginTop="12dp"
-                        android:text="@string/filedetails_size"
-                        android:textAppearance="?android:attr/textAppearanceMedium" />
-
-                    <TextView
-                        android:id="@+id/fdCreatedLabel"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_below="@+id/fdSizeLabel"
-                        android:layout_marginTop="12dp"
-                        android:text="@string/filedetails_created"
-                        android:visibility="gone"
-                        android:textAppearance="?android:attr/textAppearanceMedium" />
-                    
-                    <TextView
-                        android:id="@+id/fdModifiedLabel"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_below="@+id/fdCreatedLabel"
-                        android:layout_marginTop="12dp"
-                        android:text="@string/filedetails_modified"
-                        android:textAppearance="?android:attr/textAppearanceMedium" />
-                </RelativeLayout>
-
-                <RelativeLayout
-                    android:id="@+id/fdValueContainer"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_alignParentTop="true"
-                    android:layout_marginLeft="12dp"
-                    android:layout_toRightOf="@+id/fdLabelContainer" >
-
-                    <TextView
-                        android:id="@+id/fdType"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_marginTop="24dp"
-                        android:text="JPG Image"
-                        android:textAppearance="?android:attr/textAppearanceMedium" />
-
-                    <TextView
-                        android:id="@+id/fdSize"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_below="@+id/fdType"
-                        android:layout_marginTop="12dp"
-                        android:text="389 KB"
-                        android:textAppearance="?android:attr/textAppearanceMedium" />
-
-                    <TextView
-                        android:id="@+id/fdCreated"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_below="@+id/fdSize"
-                        android:layout_marginTop="12dp"
-                        android:visibility="gone"
-                        android:text="2012/05/18 12:23 PM"
-                        android:textAppearance="?android:attr/textAppearanceMedium" />
-
-                    <TextView
-                        android:id="@+id/fdModified"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_below="@+id/fdCreated"
-                        android:layout_marginTop="12dp"
-                        android:text="2012/05/19 02:56 PM"
-                        android:textAppearance="?android:attr/textAppearanceMedium" />
-                </RelativeLayout>
-
-            </RelativeLayout>
-
-            <RelativeLayout
-                android:id="@+id/fdPreviewAndDL"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_below="@+id/fdDetailsContainer"
-                android:gravity="center_horizontal" >
-
-                <CheckBox
-                    android:id="@+id/fdKeepInSync"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_centerHorizontal="true"
-                    android:text="@string/fd_keep_in_sync" />
-
-                <ImageView
-                    android:id="@+id/fdPreview"
-                    android:layout_width="match_parent"
-                    android:layout_height="match_parent"
-                    android:layout_below="@id/fdKeepInSync"
-                    android:layout_centerHorizontal="true"
-                    android:layout_marginTop="16dp"
-                    android:src="@drawable/owncloud_logo" />
-
-                <LinearLayout
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:layout_below="@id/fdPreview"
-                    android:orientation="vertical" >
-
-                    <LinearLayout
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:gravity="center_horizontal" >
-
-                        <Button
-                            android:id="@+id/fdRemoveBtn"
-                            android:layout_width="wrap_content"
-                            android:layout_height="wrap_content"
-                            android:layout_marginTop="12dp"
-                            android:text="@string/common_remove" />
-
-                        <Button
-                            android:id="@+id/fdOpenBtn"
-                            android:layout_width="wrap_content"
-                            android:layout_height="wrap_content"
-                            android:layout_marginTop="12dp"
-                            android:text="@string/filedetails_open" />
-
-                    </LinearLayout>
-
-                    <LinearLayout
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:gravity="center_horizontal">
-
-                        <Button
-                            android:id="@+id/fdDownloadBtn"
-                            android:layout_width="wrap_content"
-                            android:layout_height="wrap_content"
-                            android:layout_marginTop="12dp"
-                            android:text="@string/filedetails_download" />
-
-                        <Button
-                            android:id="@+id/fdRenameBtn"
-                            android:layout_width="wrap_content"
-                            android:layout_height="wrap_content"
-                            android:layout_marginTop="12dp"
-                            android:text="@string/common_rename" />
-
-                    </LinearLayout>
-<!-- 
-                    <Button
-                        android:id="@+id/fdShareBtn"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_marginTop="12dp"
-                        android:text="@string/common_share" />
- -->
-
-                </LinearLayout>
-            </RelativeLayout>
-
-        </RelativeLayout>
-    </ScrollView>
-
-</RelativeLayout>
\ No newline at end of file
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+       android:id="@+id/fdScrollView"
+       android:layout_width="fill_parent"
+       android:layout_height="fill_parent" >
+
+       <RelativeLayout
+               android:layout_width="match_parent"
+               android:layout_height="wrap_content" >
+
+               <RelativeLayout
+                       android:id="@+id/fdFileHeaderContainer"
+                       android:layout_width="match_parent"
+                       android:layout_height="wrap_content"
+                       android:layout_marginLeft="16dp"
+                       android:layout_marginTop="4dp" >
+       
+                       <ImageView
+                               android:id="@+id/fdIcon"
+                               android:layout_width="wrap_content"
+                               android:layout_height="wrap_content"
+                               android:src="@drawable/file" />
+                               
+                       <TextView
+                           android:id="@+id/fdFilename"
+                           android:layout_width="wrap_content"
+                           android:layout_height="wrap_content"
+                           android:layout_centerVertical="true"
+                           android:layout_toRightOf="@+id/fdIcon"
+                           android:text="@string/placeholder_filename"
+                           android:textAppearance="?android:attr/textAppearanceLarge" />
+                       
+               </RelativeLayout>
+       
+               <RelativeLayout
+                       android:id="@+id/fdDetailsContainer"
+                       android:layout_width="match_parent"
+                       android:layout_height="wrap_content"
+                       android:layout_below="@id/fdFileHeaderContainer" >
+               
+                       <RelativeLayout
+                               android:id="@+id/fdLabelContainer"
+                               android:layout_width="wrap_content"
+                               android:layout_height="wrap_content"
+                               android:layout_alignParentLeft="true"
+                               android:layout_alignParentTop="true"
+                               android:layout_marginLeft="16dp" >
+                               
+                               <TextView
+                                       android:id="@+id/fdTypeLabel"
+                                       android:layout_width="wrap_content"
+                                       android:layout_height="wrap_content"
+                                       android:layout_marginTop="24dp"
+                                       android:text="@string/filedetails_type"
+                                       android:textAppearance="?android:attr/textAppearanceMedium" />
+                                       
+                               <TextView
+                                       android:id="@+id/fdSizeLabel"
+                                       android:layout_width="wrap_content"
+                                       android:layout_height="wrap_content"
+                                       android:layout_below="@+id/fdTypeLabel"
+                                       android:layout_marginTop="12dp"
+                                       android:text="@string/filedetails_size"
+                                       android:textAppearance="?android:attr/textAppearanceMedium" />
+                                       
+                               <TextView
+                                       android:id="@+id/fdCreatedLabel"
+                                       android:layout_width="wrap_content"
+                                       android:layout_height="wrap_content"
+                                       android:layout_below="@+id/fdSizeLabel"
+                                       android:layout_marginTop="12dp"
+                                       android:text="@string/filedetails_created"
+                                       android:visibility="gone"
+                                       android:textAppearance="?android:attr/textAppearanceMedium" />
+                                         
+                               <TextView
+                                       android:id="@+id/fdModifiedLabel"
+                                       android:layout_width="wrap_content"
+                                       android:layout_height="wrap_content"
+                                       android:layout_below="@+id/fdCreatedLabel"
+                                       android:layout_marginTop="12dp"
+                                       android:text="@string/filedetails_modified"
+                                       android:textAppearance="?android:attr/textAppearanceMedium" />
+                       </RelativeLayout>
+       
+                       <RelativeLayout
+                               android:id="@+id/fdValueContainer"
+                               android:layout_width="wrap_content"
+                               android:layout_height="wrap_content"
+                               android:layout_alignParentTop="true"
+                               android:layout_marginLeft="12dp"
+                               android:layout_toRightOf="@+id/fdLabelContainer" >
+                               
+                               <TextView
+                                       android:id="@+id/fdType"
+                                       android:layout_width="wrap_content"
+                                       android:layout_height="wrap_content"
+                                       android:layout_marginTop="24dp"
+                                       android:text="@string/placeholder_filetype"
+                                       android:textAppearance="?android:attr/textAppearanceMedium" />
+                                       
+                               <TextView
+                                       android:id="@+id/fdSize"
+                                       android:layout_width="wrap_content"
+                                       android:layout_height="wrap_content"
+                                       android:layout_below="@+id/fdType"
+                                       android:layout_marginTop="12dp"
+                                       android:text="@string/placeholder_filesize"
+                                       android:textAppearance="?android:attr/textAppearanceMedium" />
+                                       
+                               <TextView
+                                       android:id="@+id/fdCreated"
+                                       android:layout_width="wrap_content"
+                                       android:layout_height="wrap_content"
+                                       android:layout_below="@+id/fdSize"
+                                       android:layout_marginTop="12dp"
+                                       android:visibility="gone"
+                                       android:text="@string/placeholder_timestamp"
+                                       android:textAppearance="?android:attr/textAppearanceMedium" />
+                                       
+                               <TextView
+                                       android:id="@+id/fdModified"
+                                       android:layout_width="wrap_content"
+                                       android:layout_height="wrap_content"
+                                       android:layout_below="@+id/fdCreated"
+                                       android:layout_marginTop="12dp"
+                                       android:text="@string/placeholder_timestamp"
+                                       android:textAppearance="?android:attr/textAppearanceMedium" />
+               
+                       </RelativeLayout>
+       
+               </RelativeLayout>
+       
+               <RelativeLayout
+                       android:id="@+id/fdProgressAndControl"
+                       android:layout_width="match_parent"
+                       android:layout_height="wrap_content"
+                       android:layout_below="@+id/fdDetailsContainer"
+                       android:gravity="center_horizontal" 
+                       android:layout_margin="16dp"
+                       >
+                       
+                       <CheckBox
+                               android:id="@+id/fdKeepInSync"
+                               android:layout_width="wrap_content"
+                               android:layout_height="wrap_content"
+                               android:layout_centerHorizontal="true"
+                               android:text="@string/fd_keep_in_sync" />
+
+                       <LinearLayout
+                               android:layout_width="match_parent"
+                               android:layout_height="wrap_content"
+                               android:layout_below="@id/fdKeepInSync"
+                               android:orientation="vertical" >
+                               
+                       <TextView 
+                           android:id="@+id/fdProgressText" 
+                           android:layout_width="match_parent"
+                               android:layout_height="wrap_content" 
+                               android:text="@string/downloader_download_in_progress_ticker"
+                               />
+                       
+                       <ProgressBar android:id="@+id/fdProgressBar"
+                                       android:layout_width="match_parent" 
+                                       android:layout_height="wrap_content"
+                                       android:progressDrawable="@android:drawable/progress_horizontal"
+                                       android:indeterminate="false" 
+                                       android:indeterminateOnly="false" 
+                                       />
+                                                                                               
+                           <LinearLayout
+                                       android:layout_width="match_parent"
+                                       android:layout_height="wrap_content"
+                                       android:gravity="center_horizontal"
+                                       android:layout_marginTop="12dp"
+                                       >
+                                                               
+                                       <Button
+                                               android:id="@+id/fdDownloadBtn"
+                                               android:layout_width="0dp"
+                                               android:layout_height="wrap_content"
+                                               android:layout_weight="1"
+                                               android:text="@string/filedetails_download" />
+                                               
+                                       <Button
+                                               android:id="@+id/fdOpenBtn"
+                                               android:layout_width="0dp"
+                                               android:layout_height="wrap_content"
+                                               android:layout_weight="1"
+                                               android:text="@string/filedetails_open" />
+                                               
+                               </LinearLayout>
+               
+                               <LinearLayout
+                                       android:layout_width="match_parent"
+                                       android:layout_height="wrap_content"
+                                       android:gravity="center_horizontal"
+                                       android:layout_marginTop="12dp"
+                                       >
+                                       
+                                       <Button
+                                               android:id="@+id/fdRenameBtn"
+                                               android:layout_width="0dp"
+                                               android:layout_height="wrap_content"
+                                               android:layout_weight="1"
+                                               android:text="@string/common_rename" />
+                                               
+                                   <Button
+                                               android:id="@+id/fdRemoveBtn"
+                                               android:layout_width="0dp"
+                                               android:layout_height="wrap_content"
+                                               android:layout_weight="1"
+                                               android:text="@string/common_remove" />
+                                               
+                               </LinearLayout>
+       
+                       </LinearLayout>
+                       
+               </RelativeLayout>
+
+       </RelativeLayout>
+       
+</ScrollView>
index 0d6d06e..c3dc2e1 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
diff --git a/res/layout/file_download_fragment.xml b/res/layout/file_download_fragment.xml
new file mode 100644 (file)
index 0000000..9c91c37
--- /dev/null
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ownCloud Android client application
+
+  Copyright (C) 2012-2013  ownCloud Inc.
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License 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/>.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+       android:layout_width="match_parent"
+       android:layout_height="match_parent"
+       android:orientation="vertical" 
+       android:layout_gravity="center"
+       android:gravity="center_vertical"
+       android:padding="20dp"
+       >
+       
+       <TextView 
+           android:id="@+id/progressText" 
+           android:layout_width="match_parent"
+               android:layout_height="wrap_content" 
+               android:text="@string/downloader_not_downloaded_yet"
+               android:layout_marginBottom="15dp"
+       />
+       
+       <ProgressBar android:id="@+id/progressBar"
+               android:layout_width="match_parent" 
+               android:layout_height="wrap_content"
+               android:progressDrawable="@android:drawable/progress_horizontal"
+               android:indeterminate="false" 
+               android:indeterminateOnly="false" 
+               android:layout_marginBottom="15dp"
+       />
+       
+       <Button
+               android:id="@+id/cancelBtn"
+               android:layout_width="wrap_content"
+               android:layout_height="wrap_content"
+               android:text="@string/common_cancel" 
+               android:layout_marginBottom="15dp"
+       />
+       
+       <ImageView
+               android:id="@+id/error_image"
+               android:layout_width="wrap_content"
+               android:layout_height="wrap_content"
+               android:layout_margin="0dp"
+               android:layout_gravity="center_horizontal"
+               android:contentDescription="@string/downloader_download_failed_ticker"
+               android:src="@drawable/image_fail" />
+                                                                               
+       <TextView 
+           android:id="@+id/errorText" 
+           android:layout_width="wrap_content"
+               android:layout_height="wrap_content"
+               android:layout_gravity="center_horizontal"
+               android:layout_margin="40dp"
+               android:text="@string/downloader_download_failed_ticker"
+       />
+       
+</LinearLayout>
+
diff --git a/res/layout/file_preview.xml b/res/layout/file_preview.xml
new file mode 100644 (file)
index 0000000..22528a7
--- /dev/null
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ownCloud Android client application
+
+  Copyright (C) 2012-2013  ownCloud Inc.
+  
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License 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/>.
+  
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/top"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/owncloud_white"
+    android:gravity="center"
+    tools:context=".ui.fragment.FilePreviewFragment" >
+
+    <FrameLayout 
+        android:id="@+id/visual_area"
+           android:layout_width="match_parent"
+           android:layout_height="0dp"
+           android:layout_alignParentTop="true"
+           android:layout_above="@+id/media_controller"
+        >
+    
+           <ImageView
+               android:id="@+id/image_preview"
+               android:layout_width="match_parent"
+               android:layout_height="match_parent"
+               android:layout_margin="16dp"
+               android:layout_gravity="center"
+               android:contentDescription="@string/preview_image_description"
+               android:src="@drawable/owncloud_logo" />
+           
+               <VideoView  
+                   android:id="@+id/video_preview"
+                       android:layout_width="match_parent"
+                       android:layout_height="match_parent"  
+                       android:layout_gravity="center" 
+                       android:visibility="gone"
+                       />
+               
+       </FrameLayout>
+       
+       <com.owncloud.android.media.MediaControlView 
+           android:id="@id/media_controller"
+           android:layout_width="match_parent"
+           android:layout_height="wrap_content"
+           android:layout_alignParentBottom="true"
+           />
+
+</RelativeLayout>
\ No newline at end of file
index 5633a57..0e4fd8b 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.\r
 \r
   This program is free software: you can redistribute it and/or modify\r
-  it under the terms of the GNU General Public License as published by\r
-  the Free Software Foundation, either version 2 of the License, or\r
-  (at your option) any later version.\r
+  it under the terms of the GNU General Public License version 2,\r
+  as published by the Free Software Foundation.\r
 \r
   This program is distributed in the hope that it will be useful,\r
   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index ac718e8..e218236 100644 (file)
@@ -5,9 +5,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
   
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 54c5b1d..be98f3b 100644 (file)
@@ -5,9 +5,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 46e9a33..2f25047 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.\r
 \r
   This program is free software: you can redistribute it and/or modify\r
-  it under the terms of the GNU General Public License as published by\r
-  the Free Software Foundation, either version 2 of the License, or\r
-  (at your option) any later version.\r
+  it under the terms of the GNU General Public License version 2,\r
+  as published by the Free Software Foundation.\r
 \r
   This program is distributed in the hope that it will be useful,\r
   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index 80b049c..8ac118f 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.\r
 \r
   This program is free software: you can redistribute it and/or modify\r
-  it under the terms of the GNU General Public License as published by\r
-  the Free Software Foundation, either version 2 of the License, or\r
-  (at your option) any later version.\r
+  it under the terms of the GNU General Public License version 2,\r
+  as published by the Free Software Foundation.\r
 \r
   This program is distributed in the hope that it will be useful,\r
   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index 20b317b..01b05df 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 4333d4d..ea73832 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.\r
 \r
   This program is free software: you can redistribute it and/or modify\r
-  it under the terms of the GNU General Public License as published by\r
-  the Free Software Foundation, either version 2 of the License, or\r
-  (at your option) any later version.\r
+  it under the terms of the GNU General Public License version 2,\r
+  as published by the Free Software Foundation.\r
 \r
   This program is distributed in the hope that it will be useful,\r
   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
diff --git a/res/layout/log_item.xml b/res/layout/log_item.xml
new file mode 100644 (file)
index 0000000..d9326ff
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal" >
+    
+     <TextView android:id="@+id/log_item_single"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:gravity="center_vertical"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentBottom="true"
+        android:textStyle="bold"
+        android:textSize="22dp"
+        android:textColor="#000000"
+        android:layout_marginTop="5dp"
+        android:layout_marginBottom="5dp" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/log_send_file.xml b/res/layout/log_send_file.xml
new file mode 100644 (file)
index 0000000..ddeac69
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+ <LinearLayout 
+        xmlns:android="http://schemas.android.com/apk/res/android"
+         android:orientation="vertical"
+         android:layout_width="match_parent"
+         android:layout_height="match_parent"
+         android:paddingLeft="8dp"
+         android:paddingRight="8dp">
+      
+
+     <ListView         android:id="@android:id/list"
+                       android:layout_width="match_parent"
+                       android:layout_height="wrap_content"
+               />
+
+     <TextView         android:id="@android:id/empty"
+                       android:layout_width="match_parent"
+                       android:layout_height="wrap_content"
+                       android:background="#FF0000"
+                       android:text="No data"/>
+     <LinearLayout 
+         android:orientation="vertical"
+         android:layout_width="match_parent"
+         android:layout_height="wrap_content"
+         android:gravity="bottom">
+        
+     <Button           android:id="@+id/deleteLogHistoryButton"
+                       android:layout_width="match_parent"
+                       android:layout_height="wrap_content"
+                       android:text="@string/prefs_log_delete_history_button"/>
+     
+     </LinearLayout>
+     
+ </LinearLayout>
index 568d485..a55cf59 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.\r
 \r
   This program is free software: you can redistribute it and/or modify\r
-  it under the terms of the GNU General Public License as published by\r
-  the Free Software Foundation, either version 2 of the License, or\r
-  (at your option) any later version.\r
+  it under the terms of the GNU General Public License version 2,\r
+  as published by the Free Software Foundation.\r
 \r
   This program is distributed in the hope that it will be useful,\r
   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
diff --git a/res/layout/media_control.xml b/res/layout/media_control.xml
new file mode 100644 (file)
index 0000000..489c997
--- /dev/null
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ownCloud Android client application
+
+  Copyright (C) 2012-2013  ownCloud Inc.
+  
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License 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/>.
+  
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:paddingTop="4dip"
+        android:orientation="horizontal"
+        >
+
+        <ImageButton 
+            android:id="@+id/rewindBtn" 
+            style="@android:style/MediaButton.Rew" 
+            android:contentDescription="@string/media_rewind_description"
+            />
+        <ImageButton 
+            android:id="@+id/playBtn" 
+            style="@android:style/MediaButton.Play" 
+            android:contentDescription="@string/media_play_pause_description"
+            />
+        <ImageButton 
+            android:id="@+id/forwardBtn" 
+            style="@android:style/MediaButton.Ffwd" 
+            android:contentDescription="@string/media_forward_description"
+            />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <TextView 
+            android:id="@+id/currentTimeText"
+            android:textSize="14sp"
+            android:textStyle="bold"
+            android:paddingTop="4dip"
+            android:paddingStart="4dip"
+            android:layout_gravity="center_horizontal"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingEnd="4dip"
+            android:text="@string/placeholder_media_time"
+            />
+
+        <SeekBar
+            android:id="@+id/progressBar"
+            style="?android:attr/progressBarStyleHorizontal"
+            android:layout_width="0dip"
+            android:layout_weight="1"
+            android:layout_height="32dip"
+            android:layout_alignParentStart="true"
+            android:layout_alignParentEnd="true" />
+
+        <TextView android:id="@+id/totalTimeText"
+            android:textSize="14sp"
+            android:textStyle="bold"
+            android:paddingTop="4dip"
+            android:paddingEnd="4dip"
+            android:layout_gravity="center_horizontal"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingStart="4dip"
+            android:text="@string/placeholder_media_time"
+            />
+        
+    </LinearLayout>
+
+</LinearLayout>
index ef068ef..0c9cf13 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 23a8c79..665f6c6 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 0b94645..b1b6ce8 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.\r
 \r
   This program is free software: you can redistribute it and/or modify\r
-  it under the terms of the GNU General Public License as published by\r
-  the Free Software Foundation, either version 2 of the License, or\r
-  (at your option) any later version.\r
+  it under the terms of the GNU General Public License version 2,\r
+  as published by the Free Software Foundation.\r
 \r
   This program is distributed in the hope that it will be useful,\r
   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index 2d97d28..ad0676b 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.\r
 \r
   This program is free software: you can redistribute it and/or modify\r
-  it under the terms of the GNU General Public License as published by\r
-  the Free Software Foundation, either version 2 of the License, or\r
-  (at your option) any later version.\r
+  it under the terms of the GNU General Public License version 2,\r
+  as published by the Free Software Foundation.\r
 \r
   This program is distributed in the hope that it will be useful,\r
   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
diff --git a/res/layout/preview_image_activity.xml b/res/layout/preview_image_activity.xml
new file mode 100644 (file)
index 0000000..9f8b119
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ownCloud Android client application
+
+  Copyright (C) 2012-2013  ownCloud Inc.
+  
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License 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/>.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+
+    <android.support.v4.view.ViewPager 
+        android:id="@+id/fragmentPager"
+               android:layout_width="match_parent"
+               android:layout_height="match_parent" 
+               />
+    
+    <!-- LinearLayout
+        android:id="@+id/fragment"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" >
+            <!- - Preview: layout=@layout/preview_image_fragment - ->
+    </LinearLayout -->
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/preview_image_fragment.xml b/res/layout/preview_image_fragment.xml
new file mode 100644 (file)
index 0000000..3deb2fd
--- /dev/null
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ownCloud Android client application
+
+  Copyright (C) 2012-2013  ownCloud Inc.
+  
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License 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/>.
+  
+-->
+
+<!--
+     ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/fdScrollView"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:background="@color/owncloud_white" 
+    android:gravity="center_horizontal"
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/top"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:background="@color/owncloud_white"
+    tools:context=".ui.fragment.PreviewImageFragment" >
+
+    <ProgressBar 
+        android:id="@+id/progressWheel"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:indeterminate="true"
+        android:indeterminateOnly="true"
+        android:layout_centerInParent="true"
+        />
+    
+    <ImageView
+        android:id="@+id/image"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_margin="0dp"
+        android:layout_centerInParent="true"
+        android:contentDescription="@string/preview_image_description"
+        android:src="@drawable/image_fail" />
+    
+    <TextView
+        android:id="@+id/message"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerHorizontal="true"
+        android:layout_below="@id/image"
+        android:layout_margin="40dp"
+        android:text="@string/placeholder_sentence"
+        />
+    
+</RelativeLayout>
\ No newline at end of file
index e2e4834..e5a5afe 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index d400048..bba1a62 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 3cb3557..9da017b 100644 (file)
@@ -5,10 +5,9 @@
     Copyright (C) 2012-2013 ownCloud Inc.
 
     This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 2 of the License, or
-    (at your option) any later version.
-
+    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
index 9c1613a..7e4bdd5 100644 (file)
@@ -5,9 +5,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.\r
 \r
   This program is free software: you can redistribute it and/or modify\r
-  it under the terms of the GNU General Public License as published by\r
-  the Free Software Foundation, either version 2 of the License, or\r
-  (at your option) any later version.\r
+  it under the terms of the GNU General Public License version 2,\r
+  as published by the Free Software Foundation.\r
 \r
   This program is distributed in the hope that it will be useful,\r
   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index 89d49ab..5b31499 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
@@ -22,7 +21,7 @@
        android:layout_height="wrap_content" android:orientation="vertical"
        android:layout_width="wrap_content" android:background="#fefefe"
        android:gravity="center">
-       <TextView android:layout_width="fill_parent" android:text="Choose upload directory"
+       <TextView android:layout_width="fill_parent" android:text="@string/uploader_top_message"
                android:layout_height="wrap_content" android:id="@+id/textView1" android:textColor="@android:color/black"
                android:gravity="center_horizontal"></TextView>
        <FrameLayout android:layout_height="fill_parent"
index 5ce2dca..1cb9361 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
diff --git a/res/layout/video_layout.xml b/res/layout/video_layout.xml
new file mode 100644 (file)
index 0000000..8781ad8
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout   xmlns:android="http://schemas.android.com/apk/res/android"
+                               android:layout_width="fill_parent"
+                               android:layout_height="fill_parent" >
+
+       <VideoView  android:id="@+id/videoPlayer"
+                               android:layout_width="wrap_content"
+                               android:layout_height="wrap_content"  
+                               android:layout_gravity="center" />
+       
+</FrameLayout>
\ No newline at end of file
index 90b87e6..8532600 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index a26b96d..a6ef11e 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
diff --git a/res/menu/file_actions_menu.xml b/res/menu/file_actions_menu.xml
new file mode 100644 (file)
index 0000000..e9c8688
--- /dev/null
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ownCloud Android client application
+
+  Copyright (C) 2012  Bartek Przybylski
+  Copyright (C) 2012-2013 ownCloud Inc.
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License 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/>.
+-->
+<menu  xmlns:android="http://schemas.android.com/apk/res/android">
+    
+       <item   android:id="@+id/action_open_file_with"                 android:title="@string/filedetails_open"                        android:icon="@android:drawable/ic_menu_edit"                                   android:orderInCategory="1" />
+       <item   android:id="@+id/action_download_file"                  android:title="@string/filedetails_download"                                                                                                                                            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_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_see_details"                    android:title="@string/actionbar_see_details"           android:icon="@android:drawable/ic_menu_view"                                   android:orderInCategory="1" />
+    
+</menu>
diff --git a/res/menu/file_context_menu.xml b/res/menu/file_context_menu.xml
deleted file mode 100644 (file)
index b9bfdb7..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ownCloud Android client application
-
-  Copyright (C) 2012  Bartek Przybylski
-  Copyright (C) 2012-2013 ownCloud Inc.
-
-  This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with this program.  If not, see <http://www.gnu.org/licenses/>.
--->
-<menu  xmlns:android="http://schemas.android.com/apk/res/android">
-    
-       <item   android:id="@+id/open_file_item" 
-               android:title="@string/filedetails_open" 
-               android:icon="@android:drawable/ic_menu_edit"
-       />
-       
-       <item   android:id="@+id/download_file_item" 
-               android:title="@string/filedetails_download" 
-       />
-       
-       <item   android:id="@+id/cancel_download_item" 
-               android:title="@string/common_cancel_download" 
-               android:icon="@android:drawable/ic_menu_close_clear_cancel"
-       />
-       
-       <item   android:id="@+id/cancel_upload_item" 
-               android:title="@string/common_cancel_upload" 
-               android:icon="@android:drawable/ic_menu_close_clear_cancel"
-       />
-       
-       <item   android:id="@+id/rename_file_item" 
-               android:title="@string/common_rename" 
-               android:icon="@android:drawable/ic_menu_set_as"
-       />
-       
-    <item      android:id="@+id/remove_file_item" 
-               android:title="@string/common_remove" 
-               android:icon="@android:drawable/ic_menu_delete"
-       />
-    
-</menu>
diff --git a/res/menu/main_menu.xml b/res/menu/main_menu.xml
new file mode 100644 (file)
index 0000000..538dde7
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ownCloud Android client application
+
+  Copyright (C) 2012  Bartek Przybylski
+  Copyright (C) 2012-2013 ownCloud Inc.
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License 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/>.
+-->
+<menu
+  xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/action_sync_account"        android:title="@string/actionbar_sync"          android:icon="@drawable/ic_action_refresh"                              android:orderInCategory="2" />
+    <item android:id="@+id/action_create_dir"          android:title="@string/actionbar_mkdir"         android:icon="@drawable/ic_action_create_dir"                   android:orderInCategory="2" />
+    <item android:id="@+id/action_upload"                      android:title="@string/actionbar_upload"        android:icon="@drawable/ic_action_upload"                               android:orderInCategory="2" />
+    <item android:id="@+id/action_settings"            android:title="@string/actionbar_settings"      android:icon="@android:drawable/ic_menu_preferences"    android:orderInCategory="2" />
+    
+    <!--  <item android:id="@+id/search" android:title="@string/actionbar_search" android:icon="@drawable/ic_action_search"></item>-->
+</menu>
diff --git a/res/menu/menu.xml b/res/menu/menu.xml
deleted file mode 100644 (file)
index 9ff7342..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ownCloud Android client application
-
-  Copyright (C) 2012  Bartek Przybylski
-  Copyright (C) 2012-2013 ownCloud Inc.
-
-  This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with this program.  If not, see <http://www.gnu.org/licenses/>.
--->
-<menu
-  xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:id="@+id/startSync" android:title="@string/actionbar_sync" android:icon="@drawable/ic_action_refresh"></item>
-    <item android:id="@+id/createDirectoryItem" android:title="@string/actionbar_mkdir" android:icon="@drawable/ic_action_create_dir"></item>
-    
-    <!--  <item android:id="@+id/search" android:title="@string/actionbar_search" android:icon="@drawable/ic_action_search"></item>-->
-    <item android:id="@+id/action_upload" android:title="@string/actionbar_upload" android:icon="@drawable/ic_action_upload"></item>
-    <item android:id="@+id/action_settings" android:title="@string/actionbar_settings" android:icon="@android:drawable/ic_menu_preferences"></item>
-    <item android:id="@+id/about_app" android:title="@string/about_title" android:icon="@android:drawable/ic_menu_info_details"></item>
-</menu>
index 303a763..e488902 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index c5ceda9..a54a664 100644 (file)
@@ -5,9 +5,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index dec1353..9321d52 100644 (file)
@@ -5,9 +5,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index d982d00..754cf6f 100644 (file)
@@ -5,9 +5,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 4d14021..77a5a82 100644 (file)
@@ -53,6 +53,7 @@
   <string name="setup_title">Mit Ihrer %1$s verbinden</string>
   <string name="setup_btn_connect">Verbinden</string>
   <string name="uploader_btn_upload_text">Hochladen</string>
+  <string name="uploader_top_message">Wähle Zielverzeichnis:</string>
   <string name="uploader_wrn_no_account_title">Kein Konto gefunden</string>
   <string name="uploader_wrn_no_account_text">Es sind keine %1$s-Konten auf Ihrem Gerät eingerichtet. Bitte richten Sie zuerst ein Konto ein.</string>
   <string name="uploader_wrn_no_account_setup_btn_text">Einrichten</string>
@@ -82,6 +83,8 @@
   <string name="common_save_exit">Speichern &amp; Schließen</string>
   <string name="common_exit">%1$s verlassen</string>
   <string name="common_error">Fehler</string>
+  <string name="common_loading">Wird geladen &#8230;</string>
+  <string name="common_error_unknown">Unbekannter Fehler</string>
   <string name="about_title">Über</string>
   <string name="delete_account">Account löschen</string>
   <string name="create_account">Account erstellen</string>
   <string name="downloader_download_succeeded_content">%1$s wurde erfolgreich heruntergeladen</string>
   <string name="downloader_download_failed_ticker">Herunterladen fehlgeschlagen</string>
   <string name="downloader_download_failed_content">Herunterladen von %1$s konnte nicht abgeschlossen werden</string>
+  <string name="downloader_not_downloaded_yet">Noch nicht heruntergeladen</string>
   <string name="common_choose_account">Konto auswählen</string>
   <string name="sync_string_contacts">Kontakte</string>
   <string name="sync_fail_ticker">Synchronisation fehlgeschlagen</string>
   <string name="pincode_wrong">Falsche App-PIN</string>
   <string name="pincode_removed">Die App-PIN wurde entfernt</string>
   <string name="pincode_stored">Die App-PIN wurde gespeichert</string>
+  <string name="media_notif_ticker">"%1$s Musik Player"</string>
+  <string name="media_state_playing">"%1$s (wird abgespielt)"</string>
+  <string name="media_state_loading">"%1$s (wird geladen)"</string>
+  <string name="media_event_done">"%1$s Wiedergabe beendet"</string>
+  <string name="media_err_nothing_to_play">Keine Media-Datei gefunden</string>
+  <string name="media_err_no_account">Ungültiger Account</string>
+  <string name="media_err_not_in_owncloud">Datei ist nicht in einem gültigen Account</string>
+  <string name="media_err_unsupported">Nicht unterstützter Medien-Codec</string>
+  <string name="media_err_io">Media-Datei konnte nicht gelesen werden</string>
+  <string name="media_err_malformed">Die Media-Datei ist noch nicht kodiert</string>
+  <string name="media_err_timeout">Zeitüberschreitung ist beim Abspielen aufgetreten</string>
+  <string name="media_err_invalid_progressive_playback">Media-Datei konnte nicht gestreamt werden</string>
+  <string name="media_err_unknown">Media-Datei kann nicht vom Standard Player wiedergegeben werden</string>
+  <string name="media_err_security_ex">Sicherheits-Fehler ist beim Abspielen aufgetreten %1$s</string>
+  <string name="media_err_io_ex">Eingabe-Fehler ist beim Abspielen aufgetreten %1$s</string>
+  <string name="media_err_unexpected">Unerwarteter Fehler bei der Wiedergabe %1$s</string>
+  <string name="media_rewind_description">Zurückspulen</string>
+  <string name="media_play_pause_description">Wiedergabe oder Pause</string>
+  <string name="media_forward_description">Vorspulen</string>
+       
   <string-array name="prefs_trackmydevice_intervall_keys">
     <item>15 Minuten</item>
     <item>30 Minuten</item>
   <string name="conflict_keep_both">Beide behalten</string>
   <string name="conflict_overwrite">Überschreiben</string>
   <string name="conflict_dont_upload">Nicht hochladen</string>
+  <string name="preview_image_description">Bildvorschau</string>
+  <string name="preview_image_error_unknown_format">Das Bild kann nicht angezeigt werden</string>
+  <string name="preview_image_error_out_of_memory">"Nicht genug Speicherplatz um das Bild anzuzeigen</string>
+  
+  <string name="actionbar_failed_instant_upload">Fehlgeschlagene Sofortuploads</string>  
+  <string name="failed_upload_headline_text">Fehlgeschlagene Sofortuploads</string>
+  <string name="failed_upload_headline_hint">Auflistung aller fehlgeschlagenen Softuploads</string>
+  <string name="failed_upload_all_cb">Alle auswählen</string>
+  <string name="failed_upload_headline_retryall_btn">Ausgewählte wiederholen</string>
+  <string name="failed_upload_headline_delete_all_btn">Ausgewählte löschen </string>
+  <string name="failed_upload_retry_text">Versuche ausgewählte erneut hochzuladen</string>
+  <string name="failed_upload_load_more_images">Load more Picrures</string>
+  <string name="failed_upload_retry_do_nothing_text">do nothing you are not online for instant upload</string>
 </resources>
index ca26fdc..8013575 100644 (file)
@@ -53,6 +53,7 @@
   <string name="setup_title">Mit Deiner %1$s verbinden</string>
   <string name="setup_btn_connect">Verbinden</string>
   <string name="uploader_btn_upload_text">Hochladen</string>
+  <string name="uploader_top_message">Wähle Zielverzeichnis:</string>
   <string name="uploader_wrn_no_account_title">Kein Account gefunden</string>
   <string name="uploader_wrn_no_account_text">Es sind keine %1$s-Accounts auf Deinem Gerät eingerichtet. Bitte richte zuerst ein Konto ein.</string>
   <string name="uploader_wrn_no_account_setup_btn_text">Einrichten</string>
   <string name="conflict_keep_both">Beide behalten</string>
   <string name="conflict_overwrite">Überschreiben</string>
   <string name="conflict_dont_upload">Nicht hochladen</string>
+  
+  <string name="actionbar_failed_instant_upload">Fehlgeschlagene Sofortuploads</string>  
+  <string name="failed_upload_headline_text">Fehlgeschlagene Sofortuploads</string>
+  <string name="failed_upload_headline_hint">Auflistung aller fehlgeschlagenen Softuploads</string>
+  <string name="failed_upload_all_cb">Alle auswählen</string>
+  <string name="failed_upload_headline_retryall_btn">Ausgewählte wiederholen</string>
+  <string name="failed_upload_headline_delete_all_btn">Ausgewählte löschen </string>
+  <string name="failed_upload_retry_text">Versuche ausgewählte erneut hochzuladen</string>
+  <string name="failed_upload_load_more_images">Weitere Bilder laden</string>
+  <string name="failed_upload_retry_do_nothing_text">Upload nicht gestarted, Sie sind nicht online für ein Softupload</string>
+  <string name="failed_upload_failure_text">Fehlermeldung: </string>
+  <string name="failed_upload_quota_exceeded_text">Bitte Ã¼berprüfen sie ihre Serverkonfiguration, möglicherweise ist ihre Upload Limit Ã¼berschritten</string>
 </resources>
index b23e496..cecc571 100644 (file)
@@ -5,9 +5,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 3300a20..2be262f 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 512f656..ce9f543 100644 (file)
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
+
     <string name="app_name">ownCloud</string>
     <string name="main_password">Password:</string>
     <string name="main_login">Username:</string>
@@ -13,9 +14,8 @@
     <string name="main_settings">Settings</string>
     <string name="main_tit_accsetup">Setup Account</string>
     <string name="main_wrn_accsetup">There is no account set up on your device. In order to use this App, you need to create one.</string>
-    
-    <string name="about_message">%1$s Android App\n\nversion: %2$s</string>
-    
+    <string name="about_android">%1$s Android App</string>
+    <string name="about_version">version %1$s</string>
     <string name="actionbar_sync">Refresh</string>
     <string name="actionbar_upload">Upload</string>
     <string name="actionbar_upload_from_apps">Content from other apps</string>
@@ -23,6 +23,7 @@
     <string name="actionbar_mkdir">Create directory</string>
     <string name="actionbar_search">Search</string>
     <string name="actionbar_settings">Settings</string>
+    <string name="actionbar_see_details">Details</string>
     
     <string name="prefs_category_general">General</string>
     <string name="prefs_add_session">Add new session</string>
     <string name="prefs_pincode_summary">Protect your client</string>
     <string name="prefs_instant_upload">Enable instant uploads</string>
     <string name="prefs_instant_upload_summary">Instantly upload photos taken by camera</string>
+    <string name="prefs_log_title">Enable Logging</string>
+    <string name="prefs_log_summary">This is used to log problems</string>
+    <string name="prefs_log_title_history">Logging History</string>
+    <string name="prefs_log_summary_history">This shows the recorded logs</string>
+    <string name="prefs_log_delete_history_button">Delete History</string>
     
     <string name="auth_host_url">URL</string>
     <string name="auth_username">Username</string>
     <string name="setup_title">Connect to your %1$s</string>
     <string name="setup_btn_connect">Connect</string>
     <string name="uploader_btn_upload_text">Upload</string>
+    <string name="uploader_top_message">Choose upload directory:</string>
     <string name="uploader_wrn_no_account_title">No account found</string>
     <string name="uploader_wrn_no_account_text">There are no %1$s accounts on your device. Please setup an account first.</string>
     <string name="uploader_wrn_no_account_setup_btn_text">Setup</string>
     <string name="uploader_wrn_no_account_quit_btn_text">Quit</string>
-       <string name="uploader_wrn_no_content_title">No content to upload</string>
-       <string name="uploader_wrn_no_content_text">No content was received. Nothing to upload.</string>
+    <string name="uploader_wrn_no_content_title">No content to upload</string>
+    <string name="uploader_wrn_no_content_text">No content was received. Nothing to upload.</string>
     <string name="uploader_error_forbidden_content">%1$s is not allowed to access the shared content</string>
     <string name="uploader_info_uploading">Uploading</string>
     <string name="uploader_btn_create_dir_text">Create directory for upload</string>
-       <string name="file_list_empty">There are no files in this folder.\nNew files can be added with the \"Upload\" menu option.</string>
+    <string name="file_list_empty">There are no files in this folder.\nNew files can be added with the \"Upload\" menu option.</string>
     <string name="filedetails_select_file">Tap on a file to display additional information.</string>
     <string name="filedetails_size">Size:</string>
     <string name="filedetails_type">Type:</string>
@@ -73,7 +80,7 @@
     <string name="filedetails_modified">Modified:</string>
     <string name="filedetails_download">Download</string>
     <string name="filedetails_sync_file">Refresh</string>
-       <string name="filedetails_redownload">Redownload</string>
+    <string name="filedetails_redownload">Redownload</string>
     <string name="filedetails_open">Open</string>
     <string name="filedetails_renamed_in_upload_msg">File was renamed to %1$s during upload</string>
     <string name="common_yes">Yes</string>
     <string name="common_ok">OK</string>
     <string name="common_cancel_download">Cancel download</string>
     <string name="common_cancel_upload">Cancel upload</string>
-       <string name="common_cancel">Cancel</string>
+    <string name="common_cancel">Cancel</string>
     <string name="common_save_exit">Save &amp; Exit</string>
     <string name="common_exit">Leave %1$s</string>
     <string name="common_error">Error</string>
+    <string name="common_loading">Loading &#8230;</string>
+    <string name="common_error_unknown">Unknown error</string>
     <string name="about_title">About</string>
-    
     <string name="delete_account">Delete account</string>
     <string name="create_account">Create account</string>
-    
     <string name="upload_chooser_title">Upload from &#8230;</string>
     <string name="uploader_info_dirname">Directory name</string>
-       <string name="uploader_upload_in_progress_ticker">Uploading &#8230;</string>    
-       <string name="uploader_upload_in_progress_content">%1$d%% Uploading %2$s</string>    
-       <string name="uploader_upload_succeeded_ticker">Upload succeeded</string>
+    <string name="uploader_upload_in_progress_ticker">Uploading &#8230;</string>
+    <string name="uploader_upload_in_progress_content">%1$d%% Uploading %2$s</string>
+    <string name="uploader_upload_succeeded_ticker">Upload succeeded</string>
     <string name="uploader_upload_succeeded_content_single">%1$s was successfully uploaded</string>
     <string name="uploader_upload_succeeded_content_multiple">%1$d files were successfully uploaded</string>
     <string name="uploader_upload_failed_ticker">Upload failed</string>
     <string name="downloader_download_succeeded_content">%1$s was successfully downloaded</string>
     <string name="downloader_download_failed_ticker">Download failed</string>
     <string name="downloader_download_failed_content">Download of %1$s could not be completed</string>
+    <string name="downloader_not_downloaded_yet">Not downloaded yet</string>
     <string name="common_choose_account">Choose account</string>
     <string name="sync_string_contacts">Contacts</string>
-       <string name="sync_fail_ticker">Synchronization failed</string>
+    <string name="sync_fail_ticker">Synchronization failed</string>
     <string name="sync_fail_content">Synchronization of %1$s could not be completed</string>
     <string name="sync_fail_content_unauthorized">Invalid credentials 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_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 directory 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 directory and retain the link to %4$s.\n\nListed below are the local file(s), and the the remote file(s) in %5$s they were linked to.</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 directory 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 directory and retain the link to %4$s.\n\nListed below are the local file(s), and the the remote file(s) in %5$s they were linked to.</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>
     <string name="foreign_files_local_text">"Local: %1$s"</string>
-       <string name="foreign_files_remote_text">"Remote: %1$s"</string>
-
-       <string name="upload_query_move_foreign_files">There is not space enough to copy the selected files into the %1$s folder. Would like to move them into instead? </string>       
-       
-       <string name="use_ssl">Use Secure Connection</string>
+    <string name="foreign_files_remote_text">"Remote: %1$s"</string>
+    <string name="upload_query_move_foreign_files">There is not space enough to copy the selected files into the %1$s folder. Would like to move them into instead? </string>
+    <string name="use_ssl">Use Secure Connection</string>
     <string name="location_no_provider">%1$s cannot track your device. Please check your location settings</string>
-    
     <string name="pincode_enter_pin_code">Please, insert your App PIN</string>
     <string name="pincode_enter_new_pin_code">Please, insert your new App PIN</string>
     <string name="pincode_configure_your_pin">Enter your App PIN</string>
-    <string name="pincode_configure_your_pin_explanation">The PIN will be requested every time the app is started</string> 
+    <string name="pincode_configure_your_pin_explanation">The PIN will be requested every time the app is started</string>
     <string name="pincode_reenter_your_pincode">Please, reenter your App PIN</string>
     <string name="pincode_remove_your_pincode">Remove your App PIN</string>
-    <string name="pincode_mismatch">The App PINs are not the same</string> 
+    <string name="pincode_mismatch">The App PINs are not the same</string>
     <string name="pincode_wrong">Incorrect App PIN</string>
     <string name="pincode_removed">App PIN removed</string>
     <string name="pincode_stored">App PIN stored</string>
     
+    <string name="media_notif_ticker">"%1$s music player"</string>
+    <string name="media_state_playing">"%1$s (playing)"</string>
+    <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">Too much time 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_previous_description">Previous track button</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_next_description">Next track button</string>
     <string-array name="prefs_trackmydevice_intervall_keys">
-       <item>15 Minutes</item>
-       <item>30 Minutes</item>
-       <item>60 Minutes</item>
-       </string-array>
-       
+        <item>15 Minutes</item>
+        <item>30 Minutes</item>
+        <item>60 Minutes</item>
+    </string-array>
     <string-array name="prefs_trackmydevice_intervall_values">
-       <item>15</item>
-       <item>30</item>
-       <item>60</item>
-       </string-array>
-    <string name="auth_trying_to_login">Trying to login…</string>
+        <item>15</item>
+        <item>30</item>
+        <item>60</item>
+    </string-array>
+
+    <string name="auth_trying_to_login">Trying to login...</string>
     <string name="auth_no_net_conn_title">No network connection</string>
     <string name="auth_no_net_conn_message">No network connection has been detected, check your Internet connection and try again.</string>
     <string name="auth_connect_anyway">Connect anyway</string>
     <string name="auth_nossl_plain_ok_title">Secure connection unavailable.</string>
     <string name="auth_nossl_plain_ok_message">The Application cannot establish a secure connection to the server. A non secure connection is available. You may continue or cancel.</string>
     <string name="auth_connection_established">Connection established</string>
-    <string name="auth_testing_connection">Testing connection…</string>
+    <string name="auth_testing_connection">Testing connection...</string>
     <string name="auth_not_configured_title">Malformed server configuration</string>
     <string name="auth_not_configured_message">It seems that your server instance is not correctly configured. Contact your administrator for more details.</string>
     <string name="auth_unknown_error_title">Unknown error occurred!</string>
     <string name="auth_incorrect_path_message">Application couldn\'t find a server instance at the given path. Please check your path and try again.</string>
     <string name="auth_timeout_title">The server took too long to respond</string>
     <string name="auth_incorrect_address_title">Malformed URL</string>
-       <string name="auth_ssl_general_error_title">SSL initialization failed</string>
-       <string name="auth_ssl_unverified_server_title">Unverified SSL server\'s identity</string>
-       <string name="auth_bad_oc_version_title">Unrecognized server version</string>
-       <string name="auth_wrong_connection_title">Couldn\'t establish connection</string>
-       <string name="auth_secure_connection">Secure connection established</string>
+    <string name="auth_ssl_general_error_title">SSL initialization failed</string>
+    <string name="auth_ssl_unverified_server_title">Unverified SSL server\'s identity</string>
+    <string name="auth_bad_oc_version_title">Unrecognized server version</string>
+    <string name="auth_wrong_connection_title">Couldn\'t establish connection</string>
+    <string name="auth_secure_connection">Secure connection established</string>
     <string name="auth_login_details">Login details</string>
     <string name="auth_unauthorized">Invalid credentials</string>
        <string name="auth_oauth_error">Unsuccessful authorization</string>
     <string name="crashlog_message">Application terminated unexpectedly. Would you like to submit a crash report?</string>
     <string name="crashlog_send_report">Send report</string>
     <string name="crashlog_dont_send_report">Don\'t send report</string>
-    
     <string name="extensions_avail_title">Extensions available!</string>
     <string name="extensions_avail_message">Looks like your server instance is supporting advanced extensions. Would you like to see extensions available for android ?</string>
     <string name="fd_keep_in_sync">Keep file up to date</string>
     <string name="common_share">Share</string>
     <string name="common_rename">Rename</string>
     <string name="common_remove">Remove</string>
-    
-         <string name="confirmation_remove_alert">"Do you really want to remove %1$s ?"</string>
-         <string name="confirmation_remove_folder_alert">"Do you really want to remove %1$s and its contents ?"</string>
-         <string name="confirmation_remove_local">Local only</string>
-         <string name="confirmation_remove_folder_local">Local contents only</string>
-         <string name="confirmation_remove_remote">Remove from server</string>
-         <string name="confirmation_remove_remote_and_local">Remote and local</string>
-
+    <string name="confirmation_remove_alert">"Do you really want to remove %1$s ?"</string>
+    <string name="confirmation_remove_folder_alert">"Do you really want to remove %1$s and its contents ?"</string>
+    <string name="confirmation_remove_local">Local only</string>
+    <string name="confirmation_remove_folder_local">Local contents only</string>
+    <string name="confirmation_remove_remote">Remove from server</string>
+    <string name="confirmation_remove_remote_and_local">Remote and local</string>
     <string name="remove_success_msg">"Removal succeeded"</string>
     <string name="remove_fail_msg">"Removal failed"</string>
-    
     <string name="rename_dialog_title">Enter a new name</string>
     <string name="rename_local_fail_msg">"Local copy could not be renamed; try a different name"</string>
     <string name="rename_server_fail_msg">"Rename could not be completed"</string>
-        
-    <string name="sync_file_fail_msg">Remote file could not be checked</string> 
-    <string name="sync_file_nothing_to_do_msg">File contents already synchronized</string> 
-    
+    <string name="sync_file_fail_msg">Remote file could not be checked</string>
+    <string name="sync_file_nothing_to_do_msg">File contents already synchronized</string>
     <string name="create_dir_fail_msg">Directory could not be created</string>
-    
     <string name="wait_a_moment">Wait a moment</string>
-       
     <string name="filedisplay_unexpected_bad_get_content">"Unexpected problem ; please select the file from a different app"</string>
     <string name="filedisplay_no_file_selected">No file was selected</string>
     
     <string name="ssl_validator_label_issuer">Issued by:</string>
     <string name="ssl_validator_label_CN">Common name:</string>
     <string name="ssl_validator_label_O">Organization:</string>
-       <string name="ssl_validator_label_OU">Organizational unit:</string>
-       <string name="ssl_validator_label_C">Country:</string>
-       <string name="ssl_validator_label_ST">State:</string>
-       <string name="ssl_validator_label_L">Location:</string>
+    <string name="ssl_validator_label_OU">Organizational unit:</string>
+    <string name="ssl_validator_label_C">Country:</string>
+    <string name="ssl_validator_label_ST">State:</string>
+    <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="text_placeholder">This is a placeholder</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_upload_path">/InstantUpload</string>
-    
+    <string name="instant_upload_path">/InstantUpload</string>
     <string name="conflict_title">Update conflict</string>
     <string name="conflict_message">Remote file %s is not synchronized with local file. Continuing will replace content of file on server.</string>
     <string name="conflict_keep_both">Keep both</string>
     <string name="conflict_overwrite">Overwrite</string>
     <string name="conflict_dont_upload">Don\'t upload</string>
     
+    <string name="preview_image_description">Image preview</string>
+    <string name="preview_image_error_unknown_format">This image can not be shown</string>
+    <string name="preview_image_error_out_of_memory">"Not enough memory to show this image</string>
+    
     <!-- we need to improve the communication of errors to the user -->
     <string name="error__upload__local_file_not_copied">%1$s could not be copied to %2$s local directory</string>
-    
+    <string name="actionbar_failed_instant_upload">Failed InstantUpload"</string>
+    <string name="failed_upload_headline_text">Failed instant uploads</string>
+    <string name="failed_upload_headline_hint">Summary of all failed instant uploads</string>
+    <string name="failed_upload_all_cb">select all</string>
+    <string name="failed_upload_headline_retryall_btn">retry all selected</string>
+    <string name="failed_upload_headline_delete_all_btn">delete all  selected from uploadqueue</string>
+    <string name="failed_upload_retry_text">retry to upload the image: </string>
+    <string name="failed_upload_load_more_images">Load more Picrures</string>
+    <string name="failed_upload_retry_do_nothing_text">do nothing you are not online for instant upload</string>
+       <string name="failed_upload_failure_text">Failure Message: </string>
+       <string name="failed_upload_quota_exceeded_text">Please check your server configuration,maybe your quota is exceeded.</string>
 </resources>
index ef0cc09..161f5d3 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 6ce6c6d..98d2ddd 100644 (file)
@@ -5,9 +5,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 67d02c0..2670423 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 3b168f5..78b89bb 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
     <CheckBoxPreference android:key="instant_uploading" 
                         android:title="@string/prefs_instant_upload"  
                         android:summary="@string/prefs_instant_upload_summary"/>
-    <CheckBoxPreference android:dependency="instant_uploading" android:disableDependentsState="true" android:title="@string/instant_upload_on_wifi" android:key="instant_upload_on_wifi"/>
-    
+    <CheckBoxPreference android:dependency="instant_uploading" 
+                                       android:disableDependentsState="true" 
+                                       android:title="@string/instant_upload_on_wifi" 
+                                       android:key="instant_upload_on_wifi"/>
+    <CheckBoxPreference android:key="log_to_file" 
+                        android:title="@string/prefs_log_title"  
+                        android:summary="@string/prefs_log_summary"/>
+       <Preference             android:key="log_history" 
+                        android:title="@string/prefs_log_title_history"  
+                        android:summary="@string/prefs_log_summary_history"/>
+       <Preference             android:id="@+id/about_app" 
+                                       android:title="@string/about_title" 
+                                       android:key="about_app" />
        </PreferenceCategory>
     
 
index abb6e31..8327bf0 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
index 7512477..03fedbf 100644 (file)
@@ -6,9 +6,8 @@
   Copyright (C) 2012-2013 ownCloud Inc.
 
   This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 2 of the License, or
-  (at your option) any later version.
+  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
diff --git a/setup_env.bat b/setup_env.bat
new file mode 100644 (file)
index 0000000..c0bfeb4
--- /dev/null
@@ -0,0 +1,7 @@
+git submodule init
+git submodule update
+android.bat update project -p actionbarsherlock\library --target 1
+android.bat update project -p . --target 1
+cp third_party\android-support-library\android-support-v4.jar actionbarsherlock\library\libs\android-support-v4.jar
+cd tests
+android.bat update test-project -m .. -p .
\ No newline at end of file
index ea47f15..a1d39ae 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.\r
  *\r
  *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
+ *   it under the terms of the GNU General Public License version 2,\r
+ *   as published by the Free Software Foundation.\r
  *\r
  *   This program is distributed in the hope that it will be useful,\r
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index 70a9672..d2939c8 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.\r
  *\r
  *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
+ *   it under the terms of the GNU General Public License version 2,\r
+ *   as published by the Free Software Foundation.\r
  *\r
  *   This program is distributed in the hope that it will be useful,\r
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
@@ -25,8 +24,6 @@ import java.util.HashMap;
 import java.util.HashSet;\r
 import java.util.Set;\r
 \r
-import android.util.Log;\r
-\r
 /**\r
  * A helper class for some string operations.\r
  * \r
diff --git a/src/com/owncloud/android/Log_OC.java b/src/com/owncloud/android/Log_OC.java
new file mode 100644 (file)
index 0000000..1e2937c
--- /dev/null
@@ -0,0 +1,127 @@
+package com.owncloud.android;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+import android.util.Log;
+
+
+
+public class Log_OC {
+    
+
+    private static boolean isEnabled = false;
+    private static File logFile;
+    private static File folder;
+    private static BufferedWriter buf;  
+    
+    public static void i(String TAG, String message){
+        // Printing the message to LogCat console
+        Log.i(TAG, message);
+        // Write the log message to the file
+        appendLog(TAG+" : "+message);
+    }
+
+    public static void d(String TAG, String message){
+        Log.d(TAG, message);
+        appendLog(TAG+" : "+message);
+    }
+    public static void d(String TAG, String message, Exception e) {
+        Log.d(TAG, message, e);
+        appendLog(TAG+" : "+ message+" Exception : "+e.getStackTrace());
+    }
+    public static void e(String TAG, String message){
+        Log.e(TAG, message);
+        appendLog(TAG+" : "+message);
+    }
+    
+    public static void e(String TAG, String message, Throwable e) {
+        Log.e(TAG, message, e);
+        appendLog(TAG+" : "+ message+" Exception : "+e.getStackTrace());
+    }
+    
+    public static void v(String TAG, String message){
+        Log.v(TAG, message);
+        appendLog(TAG+" : "+message);
+    }
+    
+    public static void w(String TAG, String message) {
+        Log.w(TAG,message); 
+        appendLog(TAG+" : "+message);
+    }
+    
+    public static void wtf(String TAG, String message) {
+        Log.wtf(TAG,message); 
+        appendLog(TAG+" : "+message);
+    }
+    
+    public static void startLogging(String logPath) {
+        folder = new File(logPath);
+        logFile = new File(folder+File.separator+"log.txt");
+        
+        if (!folder.exists()) {
+            folder.mkdirs();
+        }
+        if (logFile.exists()) {
+            logFile.delete();
+        }
+        try { 
+            logFile.createNewFile();
+            buf = new BufferedWriter(new FileWriter(logFile, true));
+            isEnabled = true;
+            appendPhoneInfo();
+        }catch (IOException e){ 
+            e.printStackTrace(); 
+        } 
+    }
+    
+    public static void stopLogging() {
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss",Locale.getDefault());
+        String currentDateandTime = sdf.format(new Date());
+        if (logFile != null) {
+            logFile.renameTo(new File(folder+File.separator+"Owncloud_"+currentDateandTime+".log"));
+          
+            isEnabled = false;
+            try {
+                buf.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            } 
+        
+        }
+        
+    }
+    
+    private static void appendPhoneInfo() {
+        appendLog("Model : " + android.os.Build.MODEL);
+        appendLog("Brand : " + android.os.Build.BRAND);
+        appendLog("Product : " + android.os.Build.PRODUCT);
+        appendLog("Device : " + android.os.Build.DEVICE);
+        appendLog("Version-Codename : " + android.os.Build.VERSION.CODENAME);
+        appendLog("Version-Release : " + android.os.Build.VERSION.RELEASE);
+    }
+    
+    private static void appendLog(String text) { 
+        if (isEnabled) {
+           try { 
+               buf.append(text); 
+               buf.newLine(); 
+           } catch (IOException e) { 
+               e.printStackTrace(); 
+        } 
+    }
+}
+
+    
+   
+
+  
+
+   
+   
+}
index 142daff..d7bb609 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.\r
  *\r
  *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
+ *   it under the terms of the GNU General Public License version 2,\r
+ *   as published by the Free Software Foundation.\r
  *\r
  *   This program is distributed in the hope that it will be useful,\r
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index 862ede8..2ee5b4b 100644 (file)
-/* ownCloud Android client application\r
- *   Copyright (C) 2012  Bartek Przybylski\r
- *   Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
- *\r
- *   This program is distributed in the hope that it will be useful,\r
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- *   GNU General Public License for more details.\r
- *\r
- *   You should have received a copy of the GNU General Public License\r
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-package com.owncloud.android;\r
-\r
-import java.io.File;\r
-import java.util.ArrayList;\r
-import java.util.HashMap;\r
-import java.util.LinkedList;\r
-import java.util.List;\r
-import java.util.Stack;\r
-import java.util.Vector;\r
-\r
-import com.owncloud.android.authentication.AccountAuthenticator;\r
-import com.owncloud.android.datamodel.DataStorageManager;\r
-import com.owncloud.android.datamodel.FileDataStorageManager;\r
-import com.owncloud.android.datamodel.OCFile;\r
-import com.owncloud.android.files.services.FileUploader;\r
-\r
-import android.accounts.Account;\r
-import android.accounts.AccountManager;\r
-import android.app.AlertDialog;\r
-import android.app.AlertDialog.Builder;\r
-import android.app.Dialog;\r
-import android.app.ListActivity;\r
-import android.app.ProgressDialog;\r
-import android.content.Context;\r
-import android.content.DialogInterface;\r
-import android.content.DialogInterface.OnCancelListener;\r
-import android.content.DialogInterface.OnClickListener;\r
-import android.content.Intent;\r
-import android.database.Cursor;\r
-import android.net.Uri;\r
-import android.os.Bundle;\r
-import android.os.Parcelable;\r
-import android.provider.MediaStore.Images.Media;\r
-import android.util.Log;\r
-import android.view.View;\r
-import android.view.Window;\r
-import android.widget.AdapterView;\r
-import android.widget.AdapterView.OnItemClickListener;\r
-import android.widget.Button;\r
-import android.widget.EditText;\r
-import android.widget.SimpleAdapter;\r
-import android.widget.Toast;\r
-\r
-import com.owncloud.android.R;\r
-\r
-/**\r
- * This can be used to upload things to an ownCloud instance.\r
- * \r
- * @author Bartek Przybylski\r
- * \r
- */\r
-public class Uploader extends ListActivity implements OnItemClickListener, android.view.View.OnClickListener {\r
-    private static final String TAG = "ownCloudUploader";\r
-\r
-    private Account mAccount;\r
-    private AccountManager mAccountManager;\r
-    private Stack<String> mParents;\r
-    private ArrayList<Parcelable> mStreamsToUpload;\r
-    private boolean mCreateDir;\r
-    private String mUploadPath;\r
-    private static final String[] CONTENT_PROJECTION = { Media.DATA, Media.DISPLAY_NAME, Media.MIME_TYPE, Media.SIZE };\r
-    private DataStorageManager mStorageManager;\r
-    private OCFile mFile;\r
-\r
-    private final static int DIALOG_NO_ACCOUNT = 0;\r
-    private final static int DIALOG_WAITING = 1;\r
-    private final static int DIALOG_NO_STREAM = 2;\r
-    private final static int DIALOG_MULTIPLE_ACCOUNT = 3;\r
-    //private final static int DIALOG_GET_DIRNAME = 4;\r
-\r
-    private final static int REQUEST_CODE_SETUP_ACCOUNT = 0;\r
-\r
-    @Override\r
-    protected void onCreate(Bundle savedInstanceState) {\r
-        super.onCreate(savedInstanceState);\r
-        getWindow().requestFeature(Window.FEATURE_NO_TITLE);\r
-        mParents = new Stack<String>();\r
-        mParents.add("");\r
-        /*if (getIntent().hasExtra(Intent.EXTRA_STREAM)) {\r
-            prepareStreamsToUpload();*/\r
-        if (prepareStreamsToUpload()) {\r
-            mAccountManager = (AccountManager) getSystemService(Context.ACCOUNT_SERVICE);\r
-            Account[] accounts = mAccountManager.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE);\r
-            if (accounts.length == 0) {\r
-                Log.i(TAG, "No ownCloud account is available");\r
-                showDialog(DIALOG_NO_ACCOUNT);\r
-            } else if (accounts.length > 1) {\r
-                Log.i(TAG, "More then one ownCloud is available");\r
-                showDialog(DIALOG_MULTIPLE_ACCOUNT);\r
-            } else {\r
-                mAccount = accounts[0];\r
-                mStorageManager = new FileDataStorageManager(mAccount, getContentResolver());\r
-                populateDirectoryList();\r
-            }\r
-        } else {\r
-            showDialog(DIALOG_NO_STREAM);\r
-        }\r
-    }\r
-    \r
-    @Override\r
-    protected Dialog onCreateDialog(final int id) {\r
-        final AlertDialog.Builder builder = new Builder(this);\r
-        switch (id) {\r
-        case DIALOG_WAITING:\r
-            ProgressDialog pDialog = new ProgressDialog(this);\r
-            pDialog.setIndeterminate(false);\r
-            pDialog.setCancelable(false);\r
-            pDialog.setMessage(getResources().getString(R.string.uploader_info_uploading));\r
-            return pDialog;\r
-        case DIALOG_NO_ACCOUNT:\r
-            builder.setIcon(android.R.drawable.ic_dialog_alert);\r
-            builder.setTitle(R.string.uploader_wrn_no_account_title);\r
-            builder.setMessage(String.format(getString(R.string.uploader_wrn_no_account_text), getString(R.string.app_name)));\r
-            builder.setCancelable(false);\r
-            builder.setPositiveButton(R.string.uploader_wrn_no_account_setup_btn_text, new OnClickListener() {\r
-                @Override\r
-                public void onClick(DialogInterface dialog, int which) {\r
-                    if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.ECLAIR_MR1) {\r
-                        // using string value since in API7 this\r
-                        // constatn is not defined\r
-                        // in API7 < this constatant is defined in\r
-                        // Settings.ADD_ACCOUNT_SETTINGS\r
-                        // and Settings.EXTRA_AUTHORITIES\r
-                        Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT);\r
-                        intent.putExtra("authorities", new String[] { AccountAuthenticator.AUTHORITY });\r
-                        startActivityForResult(intent, REQUEST_CODE_SETUP_ACCOUNT);\r
-                    } else {\r
-                        // since in API7 there is no direct call for\r
-                        // account setup, so we need to\r
-                        // show our own AccountSetupAcricity, get\r
-                        // desired results and setup\r
-                        // everything for ourself\r
-                        Intent intent = new Intent(getBaseContext(), AccountAuthenticator.class);\r
-                        startActivityForResult(intent, REQUEST_CODE_SETUP_ACCOUNT);\r
-                    }\r
-                }\r
-            });\r
-            builder.setNegativeButton(R.string.uploader_wrn_no_account_quit_btn_text, new OnClickListener() {\r
-                @Override\r
-                public void onClick(DialogInterface dialog, int which) {\r
-                    finish();\r
-                }\r
-            });\r
-            return builder.create();\r
-        /*case DIALOG_GET_DIRNAME:\r
-            final EditText dirName = new EditText(getBaseContext());\r
-            builder.setView(dirName);\r
-            builder.setTitle(R.string.uploader_info_dirname);\r
-            String pathToUpload;\r
-            if (mParents.empty()) {\r
-                pathToUpload = "/";\r
-            } else {\r
-                mCursor = managedQuery(Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, mParents.peek()), null,\r
-                        null, null, null);\r
-                mCursor.moveToFirst();\r
-                pathToUpload = mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta.FILE_PATH))\r
-                        + mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta.FILE_NAME)).replace(" ", "%20");   // TODO don't make this ; use WebdavUtils.encode in the right moment\r
-            }\r
-            a a = new a(pathToUpload, dirName);\r
-            builder.setPositiveButton(R.string.common_ok, a);\r
-            builder.setNegativeButton(R.string.common_cancel, new OnClickListener() {\r
-                public void onClick(DialogInterface dialog, int which) {\r
-                    dialog.cancel();\r
-                }\r
-            });\r
-            return builder.create();*/\r
-        case DIALOG_MULTIPLE_ACCOUNT:\r
-            CharSequence ac[] = new CharSequence[mAccountManager.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE).length];\r
-            for (int i = 0; i < ac.length; ++i) {\r
-                ac[i] = mAccountManager.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE)[i].name;\r
-            }\r
-            builder.setTitle(R.string.common_choose_account);\r
-            builder.setItems(ac, new OnClickListener() {\r
-                @Override\r
-                public void onClick(DialogInterface dialog, int which) {\r
-                    mAccount = mAccountManager.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE)[which];\r
-                    mStorageManager = new FileDataStorageManager(mAccount, getContentResolver());\r
-                    populateDirectoryList();\r
-                }\r
-            });\r
-            builder.setCancelable(true);\r
-            builder.setOnCancelListener(new OnCancelListener() {\r
-                @Override\r
-                public void onCancel(DialogInterface dialog) {\r
-                    dialog.cancel();\r
-                    finish();\r
-                }\r
-            });\r
-            return builder.create();\r
-        case DIALOG_NO_STREAM:\r
-            builder.setIcon(android.R.drawable.ic_dialog_alert);\r
-            builder.setTitle(R.string.uploader_wrn_no_content_title);\r
-            builder.setMessage(R.string.uploader_wrn_no_content_text);\r
-            builder.setCancelable(false);\r
-            builder.setNegativeButton(R.string.common_cancel, new OnClickListener() {\r
-                @Override\r
-                public void onClick(DialogInterface dialog, int which) {\r
-                    finish();\r
-                }\r
-            });\r
-            return builder.create();\r
-        default:\r
-            throw new IllegalArgumentException("Unknown dialog id: " + id);\r
-        }\r
-    }\r
-\r
-    class a implements OnClickListener {\r
-        String mPath;\r
-        EditText mDirname;\r
-\r
-        public a(String path, EditText dirname) {\r
-            mPath = path; \r
-            mDirname = dirname;\r
-        }\r
-\r
-        @Override\r
-        public void onClick(DialogInterface dialog, int which) {\r
-            Uploader.this.mUploadPath = mPath + mDirname.getText().toString();\r
-            Uploader.this.mCreateDir = true;\r
-            uploadFiles();\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void onBackPressed() {\r
-\r
-        if (mParents.size() <= 1) {\r
-            super.onBackPressed();\r
-            return;\r
-        } else {\r
-            mParents.pop();\r
-            populateDirectoryList();\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {\r
-        // click on folder in the list\r
-        Log.d(TAG, "on item click");\r
-        Vector<OCFile> tmpfiles = mStorageManager.getDirectoryContent(mFile);\r
-        if (tmpfiles.size() <= 0) return;\r
-        // filter on dirtype\r
-        Vector<OCFile> files = new Vector<OCFile>();\r
-        for (OCFile f : tmpfiles)\r
-            if (f.isDirectory())\r
-                files.add(f);\r
-        if (files.size() < position) {\r
-            throw new IndexOutOfBoundsException("Incorrect item selected");\r
-        }\r
-        mParents.push(files.get(position).getFileName());\r
-        populateDirectoryList();\r
-    }\r
-\r
-    @Override\r
-    public void onClick(View v) {\r
-        // click on button\r
-        switch (v.getId()) {\r
-        case R.id.uploader_choose_folder:\r
-            mUploadPath = "";   // first element in mParents is root dir, represented by ""; init mUploadPath with "/" results in a "//" prefix\r
-            for (String p : mParents)\r
-                mUploadPath += p + OCFile.PATH_SEPARATOR;\r
-            Log.d(TAG, "Uploading file to dir " + mUploadPath);\r
-\r
-            uploadFiles();\r
-\r
-            break;\r
-        /*case android.R.id.button1: // dynamic action for create aditional dir\r
-            showDialog(DIALOG_GET_DIRNAME);\r
-            break;*/\r
-        default:\r
-            throw new IllegalArgumentException("Wrong element clicked");\r
-        }\r
-    }\r
-\r
-    @Override\r
-    protected void onActivityResult(int requestCode, int resultCode, Intent data) {\r
-        super.onActivityResult(requestCode, resultCode, data);\r
-        Log.i(TAG, "result received. req: " + requestCode + " res: " + resultCode);\r
-        if (requestCode == REQUEST_CODE_SETUP_ACCOUNT) {\r
-            dismissDialog(DIALOG_NO_ACCOUNT);\r
-            if (resultCode == RESULT_CANCELED) {\r
-                finish();\r
-            }\r
-            Account[] accounts = mAccountManager.getAccountsByType(AccountAuthenticator.AUTH_TOKEN_TYPE);\r
-            if (accounts.length == 0) {\r
-                showDialog(DIALOG_NO_ACCOUNT);\r
-            } else {\r
-                // there is no need for checking for is there more then one\r
-                // account at this point\r
-                // since account setup can set only one account at time\r
-                mAccount = accounts[0];\r
-                populateDirectoryList();\r
-            }\r
-        }\r
-    }\r
-\r
-    private void populateDirectoryList() {\r
-        setContentView(R.layout.uploader_layout);\r
-\r
-        String full_path = "";\r
-        for (String a : mParents)\r
-            full_path += a + "/";\r
-        \r
-        Log.d(TAG, "Populating view with content of : " + full_path);\r
-        \r
-        mFile = mStorageManager.getFileByPath(full_path);\r
-        if (mFile != null) {\r
-            Vector<OCFile> files = mStorageManager.getDirectoryContent(mFile);\r
-            List<HashMap<String, Object>> data = new LinkedList<HashMap<String,Object>>();\r
-            for (OCFile f : files) {\r
-                HashMap<String, Object> h = new HashMap<String, Object>();\r
-                if (f.isDirectory()) {\r
-                    h.put("dirname", f.getFileName());\r
-                    data.add(h);\r
-                }\r
-            }\r
-            SimpleAdapter sa = new SimpleAdapter(this,\r
-                                                data,\r
-                                                R.layout.uploader_list_item_layout,\r
-                                                new String[] {"dirname"},\r
-                                                new int[] {R.id.textView1});\r
-            setListAdapter(sa);\r
-            Button btn = (Button) findViewById(R.id.uploader_choose_folder);\r
-            btn.setOnClickListener(this);\r
-            getListView().setOnItemClickListener(this);\r
-        }\r
-    }\r
-\r
-    private boolean prepareStreamsToUpload() {\r
-        if (getIntent().getAction().equals(Intent.ACTION_SEND)) {\r
-            mStreamsToUpload = new ArrayList<Parcelable>();\r
-            mStreamsToUpload.add(getIntent().getParcelableExtra(Intent.EXTRA_STREAM));\r
-        } else if (getIntent().getAction().equals(Intent.ACTION_SEND_MULTIPLE)) {\r
-            mStreamsToUpload = getIntent().getParcelableArrayListExtra(Intent.EXTRA_STREAM);\r
-        }\r
-        return (mStreamsToUpload != null && mStreamsToUpload.get(0) != null);\r
-    }\r
-\r
-    public void uploadFiles() {\r
-        try {\r
-            /* TODO - mCreateDir can never be true at this moment; we will replace wdc.createDirectory by CreateFolderOperation when that is fixed \r
-            WebdavClient wdc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext());\r
-            // create last directory in path if necessary\r
-            if (mCreateDir) {\r
-                wdc.createDirectory(mUploadPath);\r
-            }\r
-            */\r
-\r
-            String[] local = new String[mStreamsToUpload.size()], remote = new String[mStreamsToUpload.size()];\r
-\r
-            for (int i = 0; i < mStreamsToUpload.size(); ++i) {\r
-                Uri uri = (Uri) mStreamsToUpload.get(i);\r
-                if (uri.getScheme().equals("content")) {\r
-                    Cursor c = getContentResolver().query((Uri) mStreamsToUpload.get(i),\r
-                                                      CONTENT_PROJECTION,\r
-                                                      null,\r
-                                                      null,\r
-                                                      null);\r
-\r
-                    if (!c.moveToFirst())\r
-                        continue;\r
-\r
-                    final String display_name = c.getString(c.getColumnIndex(Media.DISPLAY_NAME)),\r
-                                data = c.getString(c.getColumnIndex(Media.DATA));\r
-                    local[i] = data;\r
-                    remote[i] = mUploadPath + display_name;\r
-                } else if (uri.getScheme().equals("file")) {\r
-                    final File file = new File(Uri.decode(uri.toString()).replace(uri.getScheme() + "://", ""));\r
-                    local[i] = file.getAbsolutePath();\r
-                    remote[i] = mUploadPath + file.getName();\r
-                }\r
-\r
-            }\r
-            Intent intent = new Intent(getApplicationContext(), FileUploader.class);\r
-            intent.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES);\r
-            intent.putExtra(FileUploader.KEY_LOCAL_FILE, local);\r
-            intent.putExtra(FileUploader.KEY_REMOTE_FILE, remote);\r
-            intent.putExtra(FileUploader.KEY_ACCOUNT, mAccount);\r
-            startService(intent);\r
-            finish();\r
-            \r
-        } catch (SecurityException e) {\r
-            String message = String.format(getString(R.string.uploader_error_forbidden_content), getString(R.string.app_name));\r
-            Toast.makeText(this, message, Toast.LENGTH_LONG).show();            \r
-        }\r
-    }\r
-\r
-}\r
+/* ownCloud Android client application
+ *   Copyright (C) 2012  Bartek Przybylski
+ *   Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License 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;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Stack;
+import java.util.Vector;
+
+import com.owncloud.android.authentication.AccountAuthenticator;
+import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.services.FileUploader;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.AlertDialog;
+import android.app.AlertDialog.Builder;
+import android.app.Dialog;
+import android.app.ListActivity;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.provider.MediaStore.Images.Media;
+import android.view.View;
+import android.view.Window;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.SimpleAdapter;
+import android.widget.Toast;
+
+import com.owncloud.android.R;
+
+/**
+ * This can be used to upload things to an ownCloud instance.
+ * 
+ * @author Bartek Przybylski
+ * 
+ */
+public class Uploader extends ListActivity implements OnItemClickListener, android.view.View.OnClickListener {
+    private static final String TAG = "ownCloudUploader";
+
+    private Account mAccount;
+    private AccountManager mAccountManager;
+    private Stack<String> mParents;
+    private ArrayList<Parcelable> mStreamsToUpload;
+    private boolean mCreateDir;
+    private String mUploadPath;
+    private static final String[] CONTENT_PROJECTION = { Media.DATA, Media.DISPLAY_NAME, Media.MIME_TYPE, Media.SIZE };
+    private DataStorageManager mStorageManager;
+    private OCFile mFile;
+
+    private final static int DIALOG_NO_ACCOUNT = 0;
+    private final static int DIALOG_WAITING = 1;
+    private final static int DIALOG_NO_STREAM = 2;
+    private final static int DIALOG_MULTIPLE_ACCOUNT = 3;
+    //private final static int DIALOG_GET_DIRNAME = 4;
+
+    private final static int REQUEST_CODE_SETUP_ACCOUNT = 0;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getWindow().requestFeature(Window.FEATURE_NO_TITLE);
+        mParents = new Stack<String>();
+        mParents.add("");
+        /*if (getIntent().hasExtra(Intent.EXTRA_STREAM)) {
+            prepareStreamsToUpload();*/
+        if (prepareStreamsToUpload()) {
+            mAccountManager = (AccountManager) getSystemService(Context.ACCOUNT_SERVICE);
+            Account[] accounts = mAccountManager.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE);
+            if (accounts.length == 0) {
+                Log_OC.i(TAG, "No ownCloud account is available");
+                showDialog(DIALOG_NO_ACCOUNT);
+            } else if (accounts.length > 1) {
+                Log_OC.i(TAG, "More then one ownCloud is available");
+                showDialog(DIALOG_MULTIPLE_ACCOUNT);
+            } else {
+                mAccount = accounts[0];
+                mStorageManager = new FileDataStorageManager(mAccount, getContentResolver());
+                populateDirectoryList();
+            }
+        } else {
+            showDialog(DIALOG_NO_STREAM);
+        }
+    }
+    
+    @Override
+    protected Dialog onCreateDialog(final int id) {
+        final AlertDialog.Builder builder = new Builder(this);
+        switch (id) {
+        case DIALOG_WAITING:
+            ProgressDialog pDialog = new ProgressDialog(this);
+            pDialog.setIndeterminate(false);
+            pDialog.setCancelable(false);
+            pDialog.setMessage(getResources().getString(R.string.uploader_info_uploading));
+            return pDialog;
+        case DIALOG_NO_ACCOUNT:
+            builder.setIcon(android.R.drawable.ic_dialog_alert);
+            builder.setTitle(R.string.uploader_wrn_no_account_title);
+            builder.setMessage(String.format(getString(R.string.uploader_wrn_no_account_text), getString(R.string.app_name)));
+            builder.setCancelable(false);
+            builder.setPositiveButton(R.string.uploader_wrn_no_account_setup_btn_text, new OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                    if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.ECLAIR_MR1) {
+                        // using string value since in API7 this
+                        // constatn is not defined
+                        // in API7 < this constatant is defined in
+                        // Settings.ADD_ACCOUNT_SETTINGS
+                        // and Settings.EXTRA_AUTHORITIES
+                        Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT);
+                        intent.putExtra("authorities", new String[] { AccountAuthenticator.AUTHORITY });
+                        startActivityForResult(intent, REQUEST_CODE_SETUP_ACCOUNT);
+                    } else {
+                        // since in API7 there is no direct call for
+                        // account setup, so we need to
+                        // show our own AccountSetupAcricity, get
+                        // desired results and setup
+                        // everything for ourself
+                        Intent intent = new Intent(getBaseContext(), AccountAuthenticator.class);
+                        startActivityForResult(intent, REQUEST_CODE_SETUP_ACCOUNT);
+                    }
+                }
+            });
+            builder.setNegativeButton(R.string.uploader_wrn_no_account_quit_btn_text, new OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                    finish();
+                }
+            });
+            return builder.create();
+        /*case DIALOG_GET_DIRNAME:
+            final EditText dirName = new EditText(getBaseContext());
+            builder.setView(dirName);
+            builder.setTitle(R.string.uploader_info_dirname);
+            String pathToUpload;
+            if (mParents.empty()) {
+                pathToUpload = "/";
+            } else {
+                mCursor = managedQuery(Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, mParents.peek()), null,
+                        null, null, null);
+                mCursor.moveToFirst();
+                pathToUpload = mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta.FILE_PATH))
+                        + mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta.FILE_NAME)).replace(" ", "%20");   // TODO don't make this ; use WebdavUtils.encode in the right moment
+            }
+            a a = new a(pathToUpload, dirName);
+            builder.setPositiveButton(R.string.common_ok, a);
+            builder.setNegativeButton(R.string.common_cancel, new OnClickListener() {
+                public void onClick(DialogInterface dialog, int which) {
+                    dialog.cancel();
+                }
+            });
+            return builder.create();*/
+        case DIALOG_MULTIPLE_ACCOUNT:
+            CharSequence ac[] = new CharSequence[mAccountManager.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE).length];
+            for (int i = 0; i < ac.length; ++i) {
+                ac[i] = mAccountManager.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE)[i].name;
+            }
+            builder.setTitle(R.string.common_choose_account);
+            builder.setItems(ac, new OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                    mAccount = mAccountManager.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE)[which];
+                    mStorageManager = new FileDataStorageManager(mAccount, getContentResolver());
+                    populateDirectoryList();
+                }
+            });
+            builder.setCancelable(true);
+            builder.setOnCancelListener(new OnCancelListener() {
+                @Override
+                public void onCancel(DialogInterface dialog) {
+                    dialog.cancel();
+                    finish();
+                }
+            });
+            return builder.create();
+        case DIALOG_NO_STREAM:
+            builder.setIcon(android.R.drawable.ic_dialog_alert);
+            builder.setTitle(R.string.uploader_wrn_no_content_title);
+            builder.setMessage(R.string.uploader_wrn_no_content_text);
+            builder.setCancelable(false);
+            builder.setNegativeButton(R.string.common_cancel, new OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                    finish();
+                }
+            });
+            return builder.create();
+        default:
+            throw new IllegalArgumentException("Unknown dialog id: " + id);
+        }
+    }
+
+    class a implements OnClickListener {
+        String mPath;
+        EditText mDirname;
+
+        public a(String path, EditText dirname) {
+            mPath = path; 
+            mDirname = dirname;
+        }
+
+        @Override
+        public void onClick(DialogInterface dialog, int which) {
+            Uploader.this.mUploadPath = mPath + mDirname.getText().toString();
+            Uploader.this.mCreateDir = true;
+            uploadFiles();
+        }
+    }
+
+    @Override
+    public void onBackPressed() {
+
+        if (mParents.size() <= 1) {
+            super.onBackPressed();
+            return;
+        } else {
+            mParents.pop();
+            populateDirectoryList();
+        }
+    }
+
+    @Override
+    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        // click on folder in the list
+        Log_OC.d(TAG, "on item click");
+        Vector<OCFile> tmpfiles = mStorageManager.getDirectoryContent(mFile);
+        if (tmpfiles.size() <= 0) return;
+        // filter on dirtype
+        Vector<OCFile> files = new Vector<OCFile>();
+        for (OCFile f : tmpfiles)
+            if (f.isDirectory())
+                files.add(f);
+        if (files.size() < position) {
+            throw new IndexOutOfBoundsException("Incorrect item selected");
+        }
+        mParents.push(files.get(position).getFileName());
+        populateDirectoryList();
+    }
+
+    @Override
+    public void onClick(View v) {
+        // click on button
+        switch (v.getId()) {
+        case R.id.uploader_choose_folder:
+            mUploadPath = "";   // first element in mParents is root dir, represented by ""; init mUploadPath with "/" results in a "//" prefix
+            for (String p : mParents)
+                mUploadPath += p + OCFile.PATH_SEPARATOR;
+            Log_OC.d(TAG, "Uploading file to dir " + mUploadPath);
+
+            uploadFiles();
+
+            break;
+        /*case android.R.id.button1: // dynamic action for create aditional dir
+            showDialog(DIALOG_GET_DIRNAME);
+            break;*/
+        default:
+            throw new IllegalArgumentException("Wrong element clicked");
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        Log_OC.i(TAG, "result received. req: " + requestCode + " res: " + resultCode);
+        if (requestCode == REQUEST_CODE_SETUP_ACCOUNT) {
+            dismissDialog(DIALOG_NO_ACCOUNT);
+            if (resultCode == RESULT_CANCELED) {
+                finish();
+            }
+            Account[] accounts = mAccountManager.getAccountsByType(AccountAuthenticator.AUTH_TOKEN_TYPE);
+            if (accounts.length == 0) {
+                showDialog(DIALOG_NO_ACCOUNT);
+            } else {
+                // there is no need for checking for is there more then one
+                // account at this point
+                // since account setup can set only one account at time
+                mAccount = accounts[0];
+                populateDirectoryList();
+            }
+        }
+    }
+
+    private void populateDirectoryList() {
+        setContentView(R.layout.uploader_layout);
+
+        String full_path = "";
+        for (String a : mParents)
+            full_path += a + "/";
+        
+        Log_OC.d(TAG, "Populating view with content of : " + full_path);
+        
+        mFile = mStorageManager.getFileByPath(full_path);
+        if (mFile != null) {
+            Vector<OCFile> files = mStorageManager.getDirectoryContent(mFile);
+            List<HashMap<String, Object>> data = new LinkedList<HashMap<String,Object>>();
+            for (OCFile f : files) {
+                HashMap<String, Object> h = new HashMap<String, Object>();
+                if (f.isDirectory()) {
+                    h.put("dirname", f.getFileName());
+                    data.add(h);
+                }
+            }
+            SimpleAdapter sa = new SimpleAdapter(this,
+                                                data,
+                                                R.layout.uploader_list_item_layout,
+                                                new String[] {"dirname"},
+                                                new int[] {R.id.textView1});
+            setListAdapter(sa);
+            Button btn = (Button) findViewById(R.id.uploader_choose_folder);
+            btn.setOnClickListener(this);
+            getListView().setOnItemClickListener(this);
+        }
+    }
+
+    private boolean prepareStreamsToUpload() {
+        if (getIntent().getAction().equals(Intent.ACTION_SEND)) {
+            mStreamsToUpload = new ArrayList<Parcelable>();
+            mStreamsToUpload.add(getIntent().getParcelableExtra(Intent.EXTRA_STREAM));
+        } else if (getIntent().getAction().equals(Intent.ACTION_SEND_MULTIPLE)) {
+            mStreamsToUpload = getIntent().getParcelableArrayListExtra(Intent.EXTRA_STREAM);
+        }
+        return (mStreamsToUpload != null && mStreamsToUpload.get(0) != null);
+    }
+
+    public void uploadFiles() {
+        try {
+            /* TODO - mCreateDir can never be true at this moment; we will replace wdc.createDirectory by CreateFolderOperation when that is fixed 
+            WebdavClient wdc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext());
+            // create last directory in path if necessary
+            if (mCreateDir) {
+                wdc.createDirectory(mUploadPath);
+            }
+            */
+
+            String[] local = new String[mStreamsToUpload.size()], remote = new String[mStreamsToUpload.size()];
+
+            for (int i = 0; i < mStreamsToUpload.size(); ++i) {
+                Uri uri = (Uri) mStreamsToUpload.get(i);
+                if (uri.getScheme().equals("content")) {
+                    Cursor c = getContentResolver().query((Uri) mStreamsToUpload.get(i),
+                                                      CONTENT_PROJECTION,
+                                                      null,
+                                                      null,
+                                                      null);
+
+                    if (!c.moveToFirst())
+                        continue;
+
+                    final String display_name = c.getString(c.getColumnIndex(Media.DISPLAY_NAME)),
+                                data = c.getString(c.getColumnIndex(Media.DATA));
+                    local[i] = data;
+                    remote[i] = mUploadPath + display_name;
+                } else if (uri.getScheme().equals("file")) {
+                    final File file = new File(Uri.decode(uri.toString()).replace(uri.getScheme() + "://", ""));
+                    local[i] = file.getAbsolutePath();
+                    remote[i] = mUploadPath + file.getName();
+                }
+
+            }
+            Intent intent = new Intent(getApplicationContext(), FileUploader.class);
+            intent.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES);
+            intent.putExtra(FileUploader.KEY_LOCAL_FILE, local);
+            intent.putExtra(FileUploader.KEY_REMOTE_FILE, remote);
+            intent.putExtra(FileUploader.KEY_ACCOUNT, mAccount);
+            startService(intent);
+            finish();
+            
+        } catch (SecurityException e) {
+            String message = String.format(getString(R.string.uploader_error_forbidden_content), getString(R.string.app_name));
+            Toast.makeText(this, message, Toast.LENGTH_LONG).show();            
+        }
+    }
+
+}
index d6acbbb..aa12993 100644 (file)
-/* ownCloud Android client application\r
- *   Copyright (C) 2012  Bartek Przybylski\r
- *   Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
- *\r
- *   This program is distributed in the hope that it will be useful,\r
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- *   GNU General Public License for more details.\r
- *\r
- *   You should have received a copy of the GNU General Public License\r
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-\r
-package com.owncloud.android.authentication;\r
-\r
-\r
-import android.accounts.*;\r
-import android.content.Context;\r
-import android.content.Intent;\r
-import android.os.Bundle;\r
-import android.util.Log;\r
-\r
-\r
-/**\r
- *  Authenticator for ownCloud accounts.\r
- * \r
- *  Controller class accessed from the system AccountManager, providing integration of ownCloud accounts with the Android system.\r
- * \r
- *  TODO - better separation in operations for OAuth-capable and regular ownCloud accounts.\r
- *  TODO - review completeness \r
- * \r
- * @author David A. Velasco\r
- */\r
-public class AccountAuthenticator extends AbstractAccountAuthenticator {\r
-    /**\r
-     * Is used by android system to assign accounts to authenticators. Should be\r
-     * used by application and all extensions.\r
-     */\r
-    public static final String ACCOUNT_TYPE = "owncloud";\r
-    public static final String AUTHORITY = "org.owncloud";\r
-    public static final String AUTH_TOKEN_TYPE = "org.owncloud";\r
-    public static final String AUTH_TOKEN_TYPE_PASSWORD = "owncloud.password";\r
-    public static final String AUTH_TOKEN_TYPE_ACCESS_TOKEN = "owncloud.oauth2.access_token";\r
-    public static final String AUTH_TOKEN_TYPE_REFRESH_TOKEN = "owncloud.oauth2.refresh_token";\r
-\r
-    public static final String KEY_AUTH_TOKEN_TYPE = "authTokenType";\r
-    public static final String KEY_REQUIRED_FEATURES = "requiredFeatures";\r
-    public static final String KEY_LOGIN_OPTIONS = "loginOptions";\r
-    public static final String KEY_ACCOUNT = "account";\r
-    /**\r
-     * Value under this key should handle path to webdav php script. Will be\r
-     * removed and usage should be replaced by combining\r
-     * {@link com.owncloud.android.authentication.AuthenticatorActivity.KEY_OC_BASE_URL} and\r
-     * {@link com.owncloud.android.utils.OwnCloudVersion}\r
-     * \r
-     * @deprecated\r
-     */\r
-    public static final String KEY_OC_URL = "oc_url";\r
-    /**\r
-     * Version should be 3 numbers separated by dot so it can be parsed by\r
-     * {@link com.owncloud.android.utils.OwnCloudVersion}\r
-     */\r
-    public static final String KEY_OC_VERSION = "oc_version";\r
-    /**\r
-     * Base url should point to owncloud installation without trailing / ie:\r
-     * http://server/path or https://owncloud.server\r
-     */\r
-    public static final String KEY_OC_BASE_URL = "oc_base_url";\r
-    /**\r
-     * Flag signaling if the ownCloud server can be accessed with OAuth2 access tokens.\r
-     */\r
-    public static final String KEY_SUPPORTS_OAUTH2 = "oc_supports_oauth2";\r
-    \r
-    private static final String TAG = AccountAuthenticator.class.getSimpleName();\r
-    \r
-    private Context mContext;\r
-\r
-    public AccountAuthenticator(Context context) {\r
-        super(context);\r
-        mContext = context;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     */\r
-    @Override\r
-    public Bundle addAccount(AccountAuthenticatorResponse response,\r
-            String accountType, String authTokenType,\r
-            String[] requiredFeatures, Bundle options)\r
-            throws NetworkErrorException {\r
-        Log.i(TAG, "Adding account with type " + accountType\r
-                + " and auth token " + authTokenType);\r
-        try {\r
-            validateAccountType(accountType);\r
-        } catch (AuthenticatorException e) {\r
-            Log.e(TAG, "Failed to validate account type " + accountType + ": "\r
-                    + e.getMessage());\r
-            e.printStackTrace();\r
-            return e.getFailureBundle();\r
-        }\r
-        final Intent intent = new Intent(mContext, AuthenticatorActivity.class);\r
-        intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);\r
-        intent.putExtra(KEY_AUTH_TOKEN_TYPE, authTokenType);\r
-        intent.putExtra(KEY_REQUIRED_FEATURES, requiredFeatures);\r
-        intent.putExtra(KEY_LOGIN_OPTIONS, options);\r
-        intent.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_CREATE);\r
-\r
-        setIntentFlags(intent);\r
-        \r
-        final Bundle bundle = new Bundle();\r
-        bundle.putParcelable(AccountManager.KEY_INTENT, intent);\r
-        return bundle;\r
-    }\r
-\r
+/* ownCloud Android client application
+ *   Copyright (C) 2012  Bartek Przybylski
+ *   Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License 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.authentication;
+
+import android.accounts.*;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import com.owncloud.android.Log_OC;
+
+/**
+ *  Authenticator for ownCloud accounts.
+ * 
+ *  Controller class accessed from the system AccountManager, providing integration of ownCloud accounts with the Android system.
+ * 
+ *  TODO - better separation in operations for OAuth-capable and regular ownCloud accounts.
+ *  TODO - review completeness 
+ * 
+ * @author David A. Velasco
+ */
+public class AccountAuthenticator extends AbstractAccountAuthenticator {
+    
+    /**
+     * Is used by android system to assign accounts to authenticators. Should be
+     * used by application and all extensions.
+     */
+    public static final String ACCOUNT_TYPE = "owncloud";
+    public static final String AUTHORITY = "org.owncloud";
+    public static final String AUTH_TOKEN_TYPE = "org.owncloud";
+    public static final String AUTH_TOKEN_TYPE_PASSWORD = "owncloud.password";
+    public static final String AUTH_TOKEN_TYPE_ACCESS_TOKEN = "owncloud.oauth2.access_token";
+    public static final String AUTH_TOKEN_TYPE_REFRESH_TOKEN = "owncloud.oauth2.refresh_token";
+
+    public static final String KEY_AUTH_TOKEN_TYPE = "authTokenType";
+    public static final String KEY_REQUIRED_FEATURES = "requiredFeatures";
+    public static final String KEY_LOGIN_OPTIONS = "loginOptions";
+    public static final String KEY_ACCOUNT = "account";
+    
+    /**
+     * Value under this key should handle path to webdav php script. Will be
+     * removed and usage should be replaced by combining
+     * {@link com.owncloud.android.authentication.AuthenticatorActivity.KEY_OC_BASE_URL} and
+     * {@link com.owncloud.android.utils.OwnCloudVersion}
+     * 
+     * @deprecated
+     */
+    public static final String KEY_OC_URL = "oc_url";
+    /**
+     * Version should be 3 numbers separated by dot so it can be parsed by
+     * {@link com.owncloud.android.utils.OwnCloudVersion}
+     */
+    public static final String KEY_OC_VERSION = "oc_version";
+    /**
+     * Base url should point to owncloud installation without trailing / ie:
+     * http://server/path or https://owncloud.server
+     */
+    public static final String KEY_OC_BASE_URL = "oc_base_url";
+    /**
+     * Flag signaling if the ownCloud server can be accessed with OAuth2 access tokens.
+     */
+    public static final String KEY_SUPPORTS_OAUTH2 = "oc_supports_oauth2";
+    
+    private static final String TAG = AccountAuthenticator.class.getSimpleName();
+    
+    private Context mContext;
+
+    public AccountAuthenticator(Context context) {
+        super(context);
+        mContext = context;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Bundle addAccount(AccountAuthenticatorResponse response,
+            String accountType, String authTokenType,
+            String[] requiredFeatures, Bundle options)
+            throws NetworkErrorException {
+        Log_OC.i(TAG, "Adding account with type " + accountType
+                + " and auth token " + authTokenType);
+        try {
+            validateAccountType(accountType);
+        } catch (AuthenticatorException e) {
+            Log_OC.e(TAG, "Failed to validate account type " + accountType + ": "
+                    + e.getMessage());
+            e.printStackTrace();
+            return e.getFailureBundle();
+        }
+        final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
+        intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
+        intent.putExtra(KEY_AUTH_TOKEN_TYPE, authTokenType);
+        intent.putExtra(KEY_REQUIRED_FEATURES, requiredFeatures);
+        intent.putExtra(KEY_LOGIN_OPTIONS, options);
+        intent.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_CREATE);
+
+        setIntentFlags(intent);
+        
+        final Bundle bundle = new Bundle();
+        bundle.putParcelable(AccountManager.KEY_INTENT, intent);
+        return bundle;
+    }
+
     /**\r
      * {@inheritDoc}\r
      */\r
@@ -127,7 +126,7 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator {
         try {\r
             validateAccountType(account.type);\r
         } catch (AuthenticatorException e) {\r
-            Log.e(TAG, "Failed to validate account type " + account.type + ": "\r
+            Log_OC.e(TAG, "Failed to validate account type " + account.type + ": "\r
                     + e.getMessage());\r
             e.printStackTrace();\r
             return e.getFailureBundle();\r
@@ -151,106 +150,104 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator {
         return null;\r
     }\r
 \r
-    /**\r
-     * {@inheritDoc}\r
-     */\r
-    @Override\r
-    public Bundle getAuthToken(AccountAuthenticatorResponse response,\r
-            Account account, String authTokenType, Bundle options)\r
-            throws NetworkErrorException {\r
-        /// validate parameters\r
-        try {\r
-            validateAccountType(account.type);\r
-            validateAuthTokenType(authTokenType);\r
-        } catch (AuthenticatorException e) {\r
-            Log.e(TAG, "Failed to validate account type " + account.type + ": "\r
-                    + e.getMessage());\r
-            e.printStackTrace();\r
-            return e.getFailureBundle();\r
-        }\r
-        \r
-        /// check if required token is stored\r
-        final AccountManager am = AccountManager.get(mContext);\r
-        String accessToken;\r
-        if (authTokenType.equals(AUTH_TOKEN_TYPE_PASSWORD)) {\r
-            accessToken = am.getPassword(account);\r
-        } else {\r
-            accessToken = am.peekAuthToken(account, authTokenType);\r
-        }\r
-        if (accessToken != null) {\r
-            final Bundle result = new Bundle();\r
-            result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);\r
-            result.putString(AccountManager.KEY_ACCOUNT_TYPE, ACCOUNT_TYPE);\r
-            result.putString(AccountManager.KEY_AUTHTOKEN, accessToken);\r
-            return result;\r
-        }\r
-        \r
-        /// if not stored, return Intent to access the AuthenticatorActivity and UPDATE the token for the account\r
-        final Intent intent = new Intent(mContext, AuthenticatorActivity.class);\r
-        intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);\r
-        intent.putExtra(KEY_AUTH_TOKEN_TYPE, authTokenType);\r
-        intent.putExtra(KEY_LOGIN_OPTIONS, options);\r
-        intent.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, account);\r
-        intent.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_TOKEN);\r
-        \r
-\r
-        final Bundle bundle = new Bundle();\r
-        bundle.putParcelable(AccountManager.KEY_INTENT, intent);\r
-        return bundle;\r
-    }\r
-\r
-    @Override\r
-    public String getAuthTokenLabel(String authTokenType) {\r
-        return null;\r
-    }\r
-\r
-    @Override\r
-    public Bundle hasFeatures(AccountAuthenticatorResponse response,\r
-            Account account, String[] features) throws NetworkErrorException {\r
-        final Bundle result = new Bundle();\r
-        result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);\r
-        return result;\r
-    }\r
-\r
-    @Override\r
-    public Bundle updateCredentials(AccountAuthenticatorResponse response,\r
-            Account account, String authTokenType, Bundle options)\r
-            throws NetworkErrorException {\r
-        final Intent intent = new Intent(mContext, AuthenticatorActivity.class);\r
-        intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE,\r
-                response);\r
-        intent.putExtra(KEY_ACCOUNT, account);\r
-        intent.putExtra(KEY_AUTH_TOKEN_TYPE, authTokenType);\r
-        intent.putExtra(KEY_LOGIN_OPTIONS, options);\r
-        setIntentFlags(intent);\r
-\r
-        final Bundle bundle = new Bundle();\r
-        bundle.putParcelable(AccountManager.KEY_INTENT, intent);\r
-        return bundle;\r
-    }\r
-\r
-    @Override\r
-    public Bundle getAccountRemovalAllowed(\r
-            AccountAuthenticatorResponse response, Account account)\r
-            throws NetworkErrorException {\r
-        return super.getAccountRemovalAllowed(response, account);\r
-    }\r
-\r
-    private void setIntentFlags(Intent intent) {\r
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\r
-        //intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);\r
-        //intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); // incompatible with the authorization code grant in OAuth\r
-        intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);\r
-        intent.addFlags(Intent.FLAG_FROM_BACKGROUND);\r
-    }\r
-\r
-    private void validateAccountType(String type)\r
-            throws UnsupportedAccountTypeException {\r
-        if (!type.equals(ACCOUNT_TYPE)) {\r
-            throw new UnsupportedAccountTypeException();\r
-        }\r
-    }\r
-\r
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Bundle getAuthToken(AccountAuthenticatorResponse response,
+            Account account, String authTokenType, Bundle options)
+            throws NetworkErrorException {
+        /// validate parameters
+        try {
+            validateAccountType(account.type);
+            validateAuthTokenType(authTokenType);
+        } catch (AuthenticatorException e) {
+            Log_OC.e(TAG, "Failed to validate account type " + account.type + ": "
+                    + e.getMessage());
+            e.printStackTrace();
+            return e.getFailureBundle();
+        }
+        
+        /// check if required token is stored
+        final AccountManager am = AccountManager.get(mContext);
+        String accessToken;
+        if (authTokenType.equals(AUTH_TOKEN_TYPE_PASSWORD)) {
+            accessToken = am.getPassword(account);
+        } else {
+            accessToken = am.peekAuthToken(account, authTokenType);
+        }
+        if (accessToken != null) {
+            final Bundle result = new Bundle();
+            result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
+            result.putString(AccountManager.KEY_ACCOUNT_TYPE, ACCOUNT_TYPE);
+            result.putString(AccountManager.KEY_AUTHTOKEN, accessToken);
+            return result;
+        }
+        
+        /// if not stored, return Intent to access the AuthenticatorActivity and UPDATE the token for the account
+        final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
+        intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
+        intent.putExtra(KEY_AUTH_TOKEN_TYPE, authTokenType);
+        intent.putExtra(KEY_LOGIN_OPTIONS, options);
+        intent.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, account);
+        intent.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_TOKEN);
+        
+
+        final Bundle bundle = new Bundle();
+        bundle.putParcelable(AccountManager.KEY_INTENT, intent);
+        return bundle;
+    }
+
+    @Override
+    public String getAuthTokenLabel(String authTokenType) {
+        return null;
+    }
+
+    @Override
+    public Bundle hasFeatures(AccountAuthenticatorResponse response,
+            Account account, String[] features) throws NetworkErrorException {
+        final Bundle result = new Bundle();
+        result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
+        return result;
+    }
+
+    @Override
+    public Bundle updateCredentials(AccountAuthenticatorResponse response,
+            Account account, String authTokenType, Bundle options)
+            throws NetworkErrorException {
+        final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
+        intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE,
+                response);
+        intent.putExtra(KEY_ACCOUNT, account);
+        intent.putExtra(KEY_AUTH_TOKEN_TYPE, authTokenType);
+        intent.putExtra(KEY_LOGIN_OPTIONS, options);
+        setIntentFlags(intent);
+
+        final Bundle bundle = new Bundle();
+        bundle.putParcelable(AccountManager.KEY_INTENT, intent);
+        return bundle;
+    }
+
+    @Override
+    public Bundle getAccountRemovalAllowed(
+            AccountAuthenticatorResponse response, Account account)
+            throws NetworkErrorException {
+        return super.getAccountRemovalAllowed(response, account);
+    }
+
+    private void setIntentFlags(Intent intent) {
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+        intent.addFlags(Intent.FLAG_FROM_BACKGROUND);
+    }
+
+    private void validateAccountType(String type)
+            throws UnsupportedAccountTypeException {
+        if (!type.equals(ACCOUNT_TYPE)) {
+            throw new UnsupportedAccountTypeException();
+        }
+    }
+
     private void validateAuthTokenType(String authTokenType)\r
             throws UnsupportedAuthTokenTypeException {\r
         if (!authTokenType.equals(AUTH_TOKEN_TYPE) &&\r
index 971d6f0..c6a77d5 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index cc1b648..ef5c21b 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.\r
  *\r
  *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
+ *   it under the terms of the GNU General Public License version 2,\r
+ *   as published by the Free Software Foundation.\r
  *\r
  *   This program is distributed in the hope that it will be useful,\r
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
@@ -20,6 +19,7 @@
 package com.owncloud.android.authentication;\r
 \r
 import com.owncloud.android.AccountUtils;\r
+import com.owncloud.android.Log_OC;\r
 import com.owncloud.android.ui.dialog.SslValidatorDialog;\r
 import com.owncloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener;\r
 import com.owncloud.android.utils.OwnCloudVersion;\r
@@ -47,7 +47,6 @@ import android.os.Bundle;
 import android.os.Handler;\r
 import android.preference.PreferenceManager;\r
 import android.text.InputType;\r
-import android.util.Log;\r
 import android.view.View;\r
 import android.view.View.OnFocusChangeListener;\r
 import android.view.Window;\r
@@ -302,7 +301,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
      */\r
     @Override\r
     protected void onNewIntent (Intent intent) {\r
-        Log.d(TAG, "onNewIntent()");\r
+        Log_OC.d(TAG, "onNewIntent()");\r
         Uri data = intent.getData();\r
         if (data != null && data.toString().startsWith(getString(R.string.oauth2_redirect_uri))) {\r
             mNewCapturedUriFromOAuth2Redirection = data;\r
@@ -477,7 +476,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             mStatusText = R.string.auth_wtf_reenter_URL;\r
             updateConnStatus();\r
             mOkButton.setEnabled(false);\r
-            Log.wtf(TAG,  "The user was allowed to click 'connect' to an unchecked server!!");\r
+            Log_OC.wtf(TAG,  "The user was allowed to click 'connect' to an unchecked server!!");\r
             return;\r
         }\r
         \r
@@ -533,7 +532,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         uriBuilder.appendQueryParameter(OAuth2Constants.KEY_SCOPE, getString(R.string.oauth2_scope));\r
         //uriBuilder.appendQueryParameter(OAuth2Constants.KEY_STATE, whateverwewant);\r
         uri = uriBuilder.build();\r
-        Log.d(TAG, "Starting browser to view " + uri.toString());\r
+        Log_OC.d(TAG, "Starting browser to view " + uri.toString());\r
         Intent i = new Intent(Intent.ACTION_VIEW, uri);\r
         startActivity(i);\r
     }\r
@@ -722,7 +721,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             \r
             /// time to test the retrieved access token on the ownCloud server\r
             mOAuthAccessToken = ((OAuth2GetAccessToken)operation).getResultTokenMap().get(OAuth2Constants.KEY_ACCESS_TOKEN);\r
-            Log.d(TAG, "Got ACCESS TOKEN: " + mOAuthAccessToken);\r
+            Log_OC.d(TAG, "Got ACCESS TOKEN: " + mOAuthAccessToken);\r
             mAuthCheckOperation = new ExistenceCheckOperation("", this, false);\r
             WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this);\r
             client.setBearerCredentials(mOAuthAccessToken);\r
@@ -731,7 +730,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         } else {\r
             updateStatusIconAndText(result);\r
             updateAuthStatus();\r
-            Log.d(TAG, "Access failed: " + result.getLogMessage());\r
+            Log_OC.d(TAG, "Access failed: " + result.getLogMessage());\r
         }\r
     }\r
 \r
@@ -752,7 +751,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         }\r
         \r
         if (result.isSuccess()) {\r
-            Log.d(TAG, "Successful access - time to save the account");\r
+            Log_OC.d(TAG, "Successful access - time to save the account");\r
 \r
             if (mAction == ACTION_CREATE) {\r
                 createAccount();\r
@@ -766,7 +765,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         } else {\r
             updateStatusIconAndText(result);\r
             updateAuthStatus();\r
-            Log.d(TAG, "Access failed: " + result.getLogMessage());\r
+            Log_OC.d(TAG, "Access failed: " + result.getLogMessage());\r
         }\r
     }\r
 \r
@@ -874,7 +873,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             break;\r
         }\r
         default:\r
-            Log.e(TAG, "Incorrect dialog called with id = " + id);\r
+            Log_OC.e(TAG, "Incorrect dialog called with id = " + id);\r
         }\r
     }\r
 \r
@@ -897,7 +896,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                         @Override\r
                         public void onCancel(DialogInterface dialog) {\r
                             /// TODO study if this is enough\r
-                            Log.i(TAG, "Login canceled");\r
+                            Log_OC.i(TAG, "Login canceled");\r
                             if (mOperationThread != null) {\r
                                 mOperationThread.interrupt();\r
                                 finish();\r
@@ -916,7 +915,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             .setOnCancelListener(new DialogInterface.OnCancelListener() {\r
                 @Override\r
                 public void onCancel(DialogInterface dialog) {\r
-                    Log.i(TAG, "Login canceled");\r
+                    Log_OC.i(TAG, "Login canceled");\r
                     finish();\r
                 }\r
             });\r
@@ -942,7 +941,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             break;\r
         }\r
         default:\r
-            Log.e(TAG, "Incorrect dialog called with id = " + id);\r
+            Log_OC.e(TAG, "Incorrect dialog called with id = " + id);\r
         }\r
         return dialog;\r
     }\r
index 227accb..f96b627 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index 3775347..31515a1 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -45,4 +44,6 @@ public interface DataStorageManager {
     public void removeDirectory(OCFile dir, boolean removeDBData, boolean removeLocalContent);
 
     public void moveDirectory(OCFile dir, String newPath);
+
+    public Vector<OCFile> getDirectoryImages(OCFile mParentFolder);
 }
index aa914c4..1b0f083 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -26,6 +25,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Vector;
 
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.db.ProviderMeta;
 import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
 import com.owncloud.android.utils.FileStorageUtils;
@@ -40,7 +40,6 @@ import android.content.OperationApplicationException;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.RemoteException;
-import android.util.Log;
 
 public class FileDataStorageManager implements DataStorageManager {
 
@@ -156,7 +155,7 @@ public class FileDataStorageManager implements DataStorageManager {
                             cv, ProviderTableMeta._ID + "=?",
                             new String[] { String.valueOf(file.getFileId()) });
                 } catch (RemoteException e) {
-                    Log.e(TAG,
+                    Log_OC.e(TAG,
                             "Fail to insert insert file to database "
                                     + e.getMessage());
                 }
@@ -171,7 +170,7 @@ public class FileDataStorageManager implements DataStorageManager {
                     result_uri = getContentProvider().insert(
                             ProviderTableMeta.CONTENT_URI_FILE, cv);
                 } catch (RemoteException e) {
-                    Log.e(TAG,
+                    Log_OC.e(TAG,
                             "Fail to insert insert file to database "
                                     + e.getMessage());
                 }
@@ -256,10 +255,10 @@ public class FileDataStorageManager implements DataStorageManager {
             }
             
         } catch (OperationApplicationException e) {
-            Log.e(TAG, "Fail to update/insert list of files to database " + e.getMessage());
+            Log_OC.e(TAG, "Fail to update/insert list of files to database " + e.getMessage());
             
         } catch (RemoteException e) {
-            Log.e(TAG, "Fail to update/insert list of files to database " + e.getMessage());
+            Log_OC.e(TAG, "Fail to update/insert list of files to database " + e.getMessage());
         }
         
         // update new id in file objects for insertions
@@ -269,7 +268,7 @@ public class FileDataStorageManager implements DataStorageManager {
                 if (results[i].uri != null) {
                     newId = Long.parseLong(results[i].uri.getPathSegments().get(1));
                     files.get(i).setFileId(newId);
-                    //Log.v(TAG, "Found and added id in insertion for " + files.get(i).getRemotePath());
+                    //Log_OC.v(TAG, "Found and added id in insertion for " + files.get(i).getRemotePath());
                 }
             }
         }
@@ -321,7 +320,7 @@ public class FileDataStorageManager implements DataStorageManager {
                             ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?",
                             new String[] { mAccount.name }, null);
                 } catch (RemoteException e) {
-                    Log.e(TAG, e.getMessage());
+                    Log_OC.e(TAG, e.getMessage());
                     return ret;
                 }
             } else {
@@ -364,7 +363,7 @@ public class FileDataStorageManager implements DataStorageManager {
                                 + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?",
                         new String[] { value, mAccount.name }, null);
             } catch (RemoteException e) {
-                Log.e(TAG,
+                Log_OC.e(TAG,
                         "Couldn't determine file existance, assuming non existance: "
                                 + e.getMessage());
                 return false;
@@ -394,7 +393,7 @@ public class FileDataStorageManager implements DataStorageManager {
                                 + "=?", new String[] { value, mAccount.name },
                         null);
             } catch (RemoteException e) {
-                Log.e(TAG, "Could not get file details: " + e.getMessage());
+                Log_OC.e(TAG, "Could not get file details: " + e.getMessage());
                 c = null;
             }
         }
@@ -517,7 +516,7 @@ public class FileDataStorageManager implements DataStorageManager {
                                                     ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + ProviderTableMeta.FILE_PATH + " LIKE ?",
                                                     new String[] { mAccount.name, dir.getRemotePath() + "%" }, null);
                 } catch (RemoteException e) {
-                    Log.e(TAG, e.getMessage());
+                    Log_OC.e(TAG, e.getMessage());
                 }
             } else {
                 c = getContentResolver().query(ProviderTableMeta.CONTENT_URI, 
@@ -558,13 +557,30 @@ public class FileDataStorageManager implements DataStorageManager {
                 }
                 
             } catch (OperationApplicationException e) {
-                Log.e(TAG, "Fail to update descendants of " + dir.getFileId() + " in database", e);
+                Log_OC.e(TAG, "Fail to update descendants of " + dir.getFileId() + " in database", e);
                 
             } catch (RemoteException e) {
-                Log.e(TAG, "Fail to update desendants of " + dir.getFileId() + " in database", e);
+                Log_OC.e(TAG, "Fail to update desendants of " + dir.getFileId() + " in database", e);
             }
             
         }
     }
 
+    @Override
+    public Vector<OCFile> getDirectoryImages(OCFile directory) {
+        Vector<OCFile> ret = new Vector<OCFile>(); 
+        if (directory != null) {
+            // TODO better implementation, filtering in the access to database (if possible) instead of here 
+            Vector<OCFile> tmp = getDirectoryContent(directory);
+            OCFile current = null; 
+            for (int i=0; i<tmp.size(); i++) {
+                current = tmp.get(i);
+                if (current.isImage()) {
+                    ret.add(current);
+                }
+            }
+        }
+        return ret;
+    }
+
 }
index 668e991..6b8e178 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -21,9 +20,11 @@ package com.owncloud.android.datamodel;
 
 import java.io.File;
 
+import com.owncloud.android.Log_OC;
+
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.Log;
+import android.webkit.MimeTypeMap;
 
 public class OCFile implements Parcelable, Comparable<OCFile> {
 
@@ -262,7 +263,7 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
      * Does nothing if the new name is null, empty or includes "/" ; or if the file is the root directory 
      */
     public void setFileName(String name) {
-        Log.d(TAG, "OCFile name changin from " + mRemotePath);
+        Log_OC.d(TAG, "OCFile name changin from " + mRemotePath);
         if (name != null && name.length() > 0 && !name.contains(PATH_SEPARATOR) && !mRemotePath.equals(PATH_SEPARATOR)) {
             String parent = (new File(getRemotePath())).getParent();
             parent = (parent.endsWith(PATH_SEPARATOR)) ? parent : parent + PATH_SEPARATOR;
@@ -270,7 +271,7 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
             if (isDirectory()) {
                 mRemotePath += PATH_SEPARATOR;
             }
-            Log.d(TAG, "OCFile name changed to " + mRemotePath);
+            Log_OC.d(TAG, "OCFile name changed to " + mRemotePath);
         }
     }
 
@@ -455,4 +456,30 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
         return 0;
     }
 
+    /** @return  'True' if the file contains audio */
+    public boolean isAudio() {
+        return (mMimeType != null && mMimeType.startsWith("audio/"));
+    }
+
+    /** @return  'True' if the file contains video */
+    public boolean isVideo() {
+        return (mMimeType != null && mMimeType.startsWith("video/"));
+    }
+
+    /** @return  'True' if the file contains an image */
+    public boolean isImage() {
+        return ((mMimeType != null && mMimeType.startsWith("image/")) ||
+                 getMimeTypeFromName().startsWith("image/"));
+    }
+    
+    public String getMimeTypeFromName() {
+        String extension = "";
+        int pos = mRemotePath.lastIndexOf('.');
+        if (pos >= 0) {
+            extension = mRemotePath.substring(pos + 1);
+        }
+        String result = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase());
+        return (result != null) ? result : "";
+    }
+
 }
index bd0fcd1..889d1db 100644 (file)
-/* ownCloud Android client application\r
- *   Copyright (C) 2011-2012  Bartek Przybylski\r
- *\r
- *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
- *\r
- *   This program is distributed in the hope that it will be useful,\r
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- *   GNU General Public License for more details.\r
- *\r
- *   You should have received a copy of the GNU General Public License\r
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-package com.owncloud.android.db;\r
-\r
-import android.content.ContentValues;\r
-import android.content.Context;\r
-import android.database.Cursor;\r
-import android.database.sqlite.SQLiteDatabase;\r
-import android.database.sqlite.SQLiteOpenHelper;\r
-\r
-/**\r
- * Custom database helper for ownCloud\r
- * \r
- * @author Bartek Przybylski\r
- * \r
- */\r
-public class DbHandler {\r
-    private SQLiteDatabase mDB;\r
-    private OpenerHelper mHelper;\r
-    private final String mDatabaseName = "ownCloud";\r
-    private final int mDatabaseVersion = 1;\r
-    \r
-    private final String TABLE_INSTANT_UPLOAD = "instant_upload";\r
-\r
-    public DbHandler(Context context) {\r
-        mHelper = new OpenerHelper(context);\r
-        mDB = mHelper.getWritableDatabase();\r
-    }\r
-\r
-    public void close() {\r
-        mDB.close();\r
-    }\r
-\r
-    public boolean putFileForLater(String filepath, String account) {\r
-        ContentValues cv = new ContentValues();\r
-        cv.put("path", filepath);\r
-        cv.put("account", account);\r
-        return mDB.insert(TABLE_INSTANT_UPLOAD, null, cv) != -1;\r
-    }\r
-    \r
-    public Cursor getAwaitingFiles() {\r
-        return mDB.query(TABLE_INSTANT_UPLOAD, null, null, null, null, null, null);\r
-    }\r
-    \r
-    public void clearFiles() {\r
-        mDB.delete(TABLE_INSTANT_UPLOAD, null, null);\r
-    }\r
-    \r
-    /**\r
-     * \r
-     * @param localPath\r
-     * @param accountName\r
-     * @return true when one or more pendin files was removed\r
-     */\r
-    public boolean removeIUPendingFile(String localPath, String accountName) {\r
-        return mDB.delete(TABLE_INSTANT_UPLOAD,\r
-                          "path = ?",\r
-                          new String[]{ localPath }) != 0;\r
-        \r
-    }\r
-    \r
-    private class OpenerHelper extends SQLiteOpenHelper {\r
-        public OpenerHelper(Context context) {\r
-            super(context, mDatabaseName, null, mDatabaseVersion);\r
-        }\r
-\r
-        @Override\r
-        public void onCreate(SQLiteDatabase db) {\r
-            db.execSQL("CREATE TABLE " + TABLE_INSTANT_UPLOAD + " ("\r
-                       + " _id INTEGER PRIMARY KEY, "\r
-                       + " path TEXT,"\r
-                       + " account TEXT);");\r
-        }\r
-\r
-        @Override\r
-        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {\r
-        }\r
-    }\r
-}\r
+/* ownCloud Android client application
+ *   Copyright (C) 2011-2012  Bartek Przybylski
+ *   Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License 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.db;
+
+import com.owncloud.android.Log_OC;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+/**
+ * Custom database helper for ownCloud
+ * 
+ * @author Bartek Przybylski
+ * 
+ */
+public class DbHandler {
+    private SQLiteDatabase mDB;
+    private OpenerHelper mHelper;
+    private final String mDatabaseName = "ownCloud";
+    private final int mDatabaseVersion = 3;
+
+    private final String TABLE_INSTANT_UPLOAD = "instant_upload";
+
+    public static final int UPLOAD_STATUS_UPLOAD_LATER = 0;
+    public static final int UPLOAD_STATUS_UPLOAD_FAILED = 1;
+
+    public DbHandler(Context context) {
+        mHelper = new OpenerHelper(context);
+        mDB = mHelper.getWritableDatabase();
+    }
+
+    public void close() {
+        mDB.close();
+    }
+
+    public boolean putFileForLater(String filepath, String account, String message) {
+        ContentValues cv = new ContentValues();
+        cv.put("path", filepath);
+        cv.put("account", account);
+        cv.put("attempt", UPLOAD_STATUS_UPLOAD_LATER);
+        cv.put("message", message);
+        long result = mDB.insert(TABLE_INSTANT_UPLOAD, null, cv);
+        Log_OC.d(TABLE_INSTANT_UPLOAD, "putFileForLater returns with: " + result + " for file: " + filepath);
+        return result != -1;
+    }
+
+    public int updateFileState(String filepath, Integer status, String message) {
+        ContentValues cv = new ContentValues();
+        cv.put("attempt", status);
+        cv.put("message", message);
+        int result = mDB.update(TABLE_INSTANT_UPLOAD, cv, "path=?", new String[] { filepath });
+        Log_OC.d(TABLE_INSTANT_UPLOAD, "updateFileState returns with: " + result + " for file: " + filepath);
+        return result;
+    }
+
+    public Cursor getAwaitingFiles() {
+        return mDB.query(TABLE_INSTANT_UPLOAD, null, "attempt=" + UPLOAD_STATUS_UPLOAD_LATER, null, null, null, null);
+    }
+
+    public Cursor getFailedFiles() {
+        return mDB.query(TABLE_INSTANT_UPLOAD, null, "attempt>" + UPLOAD_STATUS_UPLOAD_LATER, null, null, null, null);
+    }
+
+    public void clearFiles() {
+        mDB.delete(TABLE_INSTANT_UPLOAD, null, null);
+    }
+
+    /**
+     * 
+     * @param localPath
+     * @return true when one or more pending files was removed
+     */
+    public boolean removeIUPendingFile(String localPath) {
+        long result = mDB.delete(TABLE_INSTANT_UPLOAD, "path = ?", new String[] { localPath });
+        Log_OC.d(TABLE_INSTANT_UPLOAD, "delete returns with: " + result + " for file: " + localPath);
+        return result != 0;
+
+    }
+
+    private class OpenerHelper extends SQLiteOpenHelper {
+        public OpenerHelper(Context context) {
+            super(context, mDatabaseName, null, mDatabaseVersion);
+        }
+
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+            db.execSQL("CREATE TABLE " + TABLE_INSTANT_UPLOAD + " (" + " _id INTEGER PRIMARY KEY, " + " path TEXT,"
+                    + " account TEXT,attempt INTEGER,message TEXT);");
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            if (oldVersion < 2) {
+                db.execSQL("ALTER TABLE " + TABLE_INSTANT_UPLOAD + " ADD COLUMN attempt INTEGER;");
+            }
+            db.execSQL("ALTER TABLE " + TABLE_INSTANT_UPLOAD + " ADD COLUMN message TEXT;");
+
+        }
+    }
+}
index 279f2ba..b8058b8 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.\r
  *\r
  *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
+ *   it under the terms of the GNU General Public License version 2,\r
+ *   as published by the Free Software Foundation.\r
  *\r
  *   This program is distributed in the hope that it will be useful,\r
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index d57fc7f..7b39931 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index ccef340..15db896 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
 
 package com.owncloud.android.extensions;
 
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.R;
 import android.content.Intent;
 import android.os.Bundle;
 import android.support.v4.app.DialogFragment;
-import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -62,7 +61,7 @@ public class ExtensionsAvailableDialog extends DialogFragment implements
             getActivity().finish();
             break;
         default:
-            Log.e("EAD", "Button with unknown id clicked " + v.getId());
+            Log_OC.e("EAD", "Button with unknown id clicked " + v.getId());
         }
     }
 
index 5b16729..2f7290e 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -28,6 +27,7 @@ import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.utils.OwnCloudVersion;
 
 
@@ -35,7 +35,6 @@ import android.R;
 import android.app.ListActivity;
 import android.os.Bundle;
 import android.os.Handler;
-import android.util.Log;
 import android.widget.SimpleAdapter;
 
 public class ExtensionsListActivity extends ListActivity {
@@ -81,7 +80,7 @@ public class ExtensionsListActivity extends ListActivity {
             final JSONArray ar;
             try {
                 hc.executeMethod(gm);
-                Log.e("ASD", gm.getResponseBodyAsString() + "");
+                Log_OC.e("ASD", gm.getResponseBodyAsString() + "");
                 ar = new JSONObject(gm.getResponseBodyAsString())
                         .getJSONArray("apps");
             } catch (Exception e) {
index e322fa5..8a8c430 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
 
 package com.owncloud.android.files;
 
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.files.services.FileObserverService;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.util.Log;
 
 public class BootupBroadcastReceiver extends BroadcastReceiver {
 
@@ -33,15 +32,15 @@ public class BootupBroadcastReceiver extends BroadcastReceiver {
     @Override
     public void onReceive(Context context, Intent intent) {
         if (!intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
-            Log.wtf(TAG, "Incorrect action sent " + intent.getAction());
+            Log_OC.wtf(TAG, "Incorrect action sent " + intent.getAction());
             return;
         }
-        Log.d(TAG, "Starting file observer service...");
+        Log_OC.d(TAG, "Starting file observer service...");
         Intent i = new Intent(context, FileObserverService.class);
         i.putExtra(FileObserverService.KEY_FILE_CMD,
                    FileObserverService.CMD_INIT_OBSERVED_LIST);
         context.startService(i);
-        Log.d(TAG, "DONE");
+        Log_OC.d(TAG, "DONE");
     }
 
 }
index 781402d..c9272b6 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -36,22 +35,20 @@ import android.net.ConnectivityManager;
 import android.net.NetworkInfo.State;
 import android.preference.PreferenceManager;
 import android.provider.MediaStore.Images.Media;
-import android.util.Log;
 import android.webkit.MimeTypeMap;
 
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.utils.FileStorageUtils;
+
 public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
 
-    public static String INSTANT_UPLOAD_DIR = "/InstantUpload/";
     private static String TAG = "PhotoTakenBroadcastReceiver";
-    private static final String[] CONTENT_PROJECTION = { Media.DATA,
-                                                         Media.DISPLAY_NAME,
-                                                         Media.MIME_TYPE,
-                                                         Media.SIZE };
+    private static final String[] CONTENT_PROJECTION = { Media.DATA, Media.DISPLAY_NAME, Media.MIME_TYPE, Media.SIZE };
     private static String NEW_PHOTO_ACTION = "com.android.camera.NEW_PICTURE";
-    
+
     @Override
     public void onReceive(Context context, Intent intent) {
-        Log.d(TAG, "Received: " + intent.getAction());
+        Log_OC.d(TAG, "Received: " + intent.getAction());
         if (intent.getAction().equals(android.net.ConnectivityManager.CONNECTIVITY_ACTION)) {
             handleConnectivityAction(context, intent);
         } else if (intent.getAction().equals(NEW_PHOTO_ACTION)) {
@@ -59,7 +56,7 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
         } else if (intent.getAction().equals(FileUploader.UPLOAD_FINISH_MESSAGE)) {
             handleUploadFinished(context, intent);
         } else {
-            Log.e(TAG, "Incorrect intent sent: " + intent.getAction());
+            Log_OC.e(TAG, "Incorrect intent sent: " + intent.getAction());
         }
     }
 
@@ -68,9 +65,8 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
         if (intent.getBooleanExtra(FileUploader.EXTRA_UPLOAD_RESULT, false)) {
             DbHandler db = new DbHandler(context);
             String localPath = intent.getStringExtra(FileUploader.EXTRA_OLD_FILE_PATH);
-            if (!db.removeIUPendingFile(localPath,
-                                        intent.getStringExtra(FileUploader.ACCOUNT_NAME))) {
-                Log.w(TAG, "Tried to remove non existing instant upload file " + localPath);
+            if (!db.removeIUPendingFile(localPath)) {
+                Log_OC.w(TAG, "Tried to remove non existing instant upload file " + localPath);
             }
             db.close();
         }
@@ -78,22 +74,20 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
 
     private void handleNewPhotoAction(Context context, Intent intent) {
         if (!instantUploadEnabled(context)) {
-            Log.d(TAG, "Instant upload disabled, abording uploading");
+            Log_OC.d(TAG, "Instant upload disabled, abording uploading");
             return;
         }
 
         Account account = AccountUtils.getCurrentOwnCloudAccount(context);
         if (account == null) {
-            Log.w(TAG, "No owncloud account found for instant upload, aborting");
+            Log_OC.w(TAG, "No owncloud account found for instant upload, aborting");
             return;
         }
 
-        Cursor c = context.getContentResolver().query(intent.getData(),
-                                                      CONTENT_PROJECTION,
-                                                      null, null, null);
-        
+        Cursor c = context.getContentResolver().query(intent.getData(), CONTENT_PROJECTION, null, null, null);
+
         if (!c.moveToFirst()) {
-            Log.e(TAG, "Couldn't resolve given uri: " + intent.getDataString());
+            Log_OC.e(TAG, "Couldn't resolve given uri: " + intent.getDataString());
             return;
         }
 
@@ -102,29 +96,32 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
         String mime_type = c.getString(c.getColumnIndex(Media.MIME_TYPE));
 
         c.close();
-        Log.e(TAG, file_path+"");
-        
-        if (!isOnline(context) ||
-            (instantUploadViaWiFiOnly(context) && !isConnectedViaWiFi(context))) {
-            DbHandler db = new DbHandler(context);
-            db.putFileForLater(file_path, account.name);
-            db.close();
+        Log_OC.e(TAG, file_path + "");
+
+        // same always temporally the picture to upload
+        DbHandler db = new DbHandler(context);
+        db.putFileForLater(file_path, account.name, null);
+        db.close();
+
+        if (!isOnline(context) || (instantUploadViaWiFiOnly(context) && !isConnectedViaWiFi(context))) {
             return;
         }
-        
+
         // register for upload finishe message
-        // there is a litte problem with android API, we can register for particular
-        // intent in registerReceiver but we cannot unregister from precise intent
+        // there is a litte problem with android API, we can register for
+        // particular
+        // intent in registerReceiver but we cannot unregister from precise
+        // intent
         // we can unregister from entire listenings but thats suck a bit.
         // On the other hand this might be only for dynamicly registered
         // broadcast receivers, needs investigation.
         IntentFilter filter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE);
         context.getApplicationContext().registerReceiver(this, filter);
-                
+
         Intent i = new Intent(context, FileUploader.class);
         i.putExtra(FileUploader.KEY_ACCOUNT, account);
         i.putExtra(FileUploader.KEY_LOCAL_FILE, file_path);
-        i.putExtra(FileUploader.KEY_REMOTE_FILE, INSTANT_UPLOAD_DIR + file_name);
+        i.putExtra(FileUploader.KEY_REMOTE_FILE, FileStorageUtils.getInstantUploadFilePath(context, file_name));
         i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
         i.putExtra(FileUploader.KEY_MIME_TYPE, mime_type);
         i.putExtra(FileUploader.KEY_INSTANT_UPLOAD, true);
@@ -134,14 +131,13 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
 
     private void handleConnectivityAction(Context context, Intent intent) {
         if (!instantUploadEnabled(context)) {
-            Log.d(TAG, "Instant upload disabled, abording uploading");
+            Log_OC.d(TAG, "Instant upload disabled, abording uploading");
             return;
         }
-        
-        if (!intent.hasExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY) && 
-            isOnline(context) &&
-             (!instantUploadViaWiFiOnly(context) ||
-              (instantUploadViaWiFiOnly(context) == isConnectedViaWiFi(context) == true)) ) {
+
+        if (!intent.hasExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY)
+                && isOnline(context)
+                && (!instantUploadViaWiFiOnly(context) || (instantUploadViaWiFiOnly(context) == isConnectedViaWiFi(context) == true))) {
             DbHandler db = new DbHandler(context);
             Cursor c = db.getAwaitingFiles();
             if (c.moveToFirst()) {
@@ -156,52 +152,51 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
 
                         String mimeType = null;
                         try {
-                            mimeType = MimeTypeMap.getSingleton()
-                                    .getMimeTypeFromExtension(
-                                            f.getName().substring(f.getName().lastIndexOf('.') + 1));
-                        
+                            mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
+                                    f.getName().substring(f.getName().lastIndexOf('.') + 1));
+
                         } catch (Throwable e) {
-                            Log.e(TAG, "Trying to find out MIME type of a file without extension: " + f.getName());
+                            Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + f.getName());
                         }
                         if (mimeType == null)
                             mimeType = "application/octet-stream";
-                        
+
                         Intent i = new Intent(context, FileUploader.class);
                         i.putExtra(FileUploader.KEY_ACCOUNT, account);
                         i.putExtra(FileUploader.KEY_LOCAL_FILE, file_path);
-                        i.putExtra(FileUploader.KEY_REMOTE_FILE, INSTANT_UPLOAD_DIR + f.getName());
+                        i.putExtra(FileUploader.KEY_REMOTE_FILE, FileStorageUtils.getInstantUploadFilePath(context, f.getName()));
                         i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
                         i.putExtra(FileUploader.KEY_INSTANT_UPLOAD, true);
                         context.startService(i);
-                        
+
                     } else {
-                        Log.w(TAG, "Instant upload file " + f.getAbsolutePath() + " dont exist anymore");
+                        Log_OC.w(TAG, "Instant upload file " + f.getAbsolutePath() + " dont exist anymore");
                     }
-                } while(c.moveToNext());
+                } while (c.moveToNext());
             }
             c.close();
             db.close();
         }
-        
+
     }
 
-    private boolean isOnline(Context context) {
+    public static boolean isOnline(Context context) {
         ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
         return cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnected();
     }
-    
-    private boolean isConnectedViaWiFi(Context context) {
+
+    public static boolean isConnectedViaWiFi(Context context) {
         ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
-        return cm != null && cm.getActiveNetworkInfo() != null &&
-               cm.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI &&
-               cm.getActiveNetworkInfo().getState() == State.CONNECTED;
+        return cm != null && cm.getActiveNetworkInfo() != null
+                && cm.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI
+                && cm.getActiveNetworkInfo().getState() == State.CONNECTED;
     }
-    
-    private boolean instantUploadEnabled(Context context) {
+
+    public static boolean instantUploadEnabled(Context context) {
         return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_uploading", false);
     }
-    
-    private boolean instantUploadViaWiFiOnly(Context context) {
+
+    public static boolean instantUploadViaWiFiOnly(Context context) {
         return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_upload_on_wifi", false);
     }
 }
index 8f987fa..2ea90c9 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -21,6 +20,7 @@ package com.owncloud.android.files;
 
 import java.io.File;
 
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.operations.RemoteOperationResult;
@@ -33,7 +33,6 @@ import android.accounts.Account;
 import android.content.Context;
 import android.content.Intent;
 import android.os.FileObserver;
-import android.util.Log;
 
 public class OwnCloudFileObserver extends FileObserver {
 
@@ -69,9 +68,9 @@ public class OwnCloudFileObserver extends FileObserver {
     
     @Override
     public void onEvent(int event, String path) {
-        Log.d(TAG, "Got file modified with event " + event + " and path " + mPath + ((path != null) ? File.separator + path : ""));
+        Log_OC.d(TAG, "Got file modified with event " + event + " and path " + mPath + ((path != null) ? File.separator + path : ""));
         if ((event & mMask) == 0) {
-            Log.wtf(TAG, "Incorrect event " + event + " sent for file " + mPath + ((path != null) ? File.separator + path : "") +
+            Log_OC.wtf(TAG, "Incorrect event " + event + " sent for file " + mPath + ((path != null) ? File.separator + path : "") +
                          " with registered for " + mMask + " and original path " +
                          mPath);
             return;
index 0bbb6bb..55e98ee 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index 00d5f19..cf88ce0 100644 (file)
-/* ownCloud Android client application\r
- *   Copyright (C) 2012 Bartek Przybylski\r
- *   Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
- *\r
- *   This program is distributed in the hope that it will be useful,\r
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- *   GNU General Public License for more details.\r
- *\r
- *   You should have received a copy of the GNU General Public License\r
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-\r
-package com.owncloud.android.files.services;\r
-\r
-import java.io.File;\r
-import java.io.IOException;\r
-import java.util.AbstractList;\r
-import java.util.Iterator;\r
-import java.util.Vector;\r
-import java.util.concurrent.ConcurrentHashMap;\r
-import java.util.concurrent.ConcurrentMap;\r
-\r
-import com.owncloud.android.authentication.AuthenticatorActivity;\r
-import com.owncloud.android.datamodel.FileDataStorageManager;\r
-import com.owncloud.android.datamodel.OCFile;\r
-import eu.alefzero.webdav.OnDatatransferProgressListener;\r
-\r
-import com.owncloud.android.network.OwnCloudClientUtils;\r
-import com.owncloud.android.operations.DownloadFileOperation;\r
-import com.owncloud.android.operations.RemoteOperationResult;\r
-import com.owncloud.android.operations.RemoteOperationResult.ResultCode;\r
-import com.owncloud.android.ui.activity.FileDetailActivity;\r
-import com.owncloud.android.ui.fragment.FileDetailFragment;\r
-\r
-import android.accounts.Account;\r
-import android.accounts.AccountsException;\r
-import android.app.Notification;\r
-import android.app.NotificationManager;\r
-import android.app.PendingIntent;\r
-import android.app.Service;\r
-import android.content.Intent;\r
-import android.os.Binder;\r
-import android.os.Handler;\r
-import android.os.HandlerThread;\r
-import android.os.IBinder;\r
-import android.os.Looper;\r
-import android.os.Message;\r
-import android.os.Process;\r
-import android.util.Log;\r
-import android.widget.RemoteViews;\r
-\r
-import com.owncloud.android.R;\r
-import eu.alefzero.webdav.WebdavClient;\r
-\r
-public class FileDownloader extends Service implements OnDatatransferProgressListener {\r
-    \r
-    public static final String EXTRA_ACCOUNT = "ACCOUNT";\r
-    public static final String EXTRA_FILE = "FILE";\r
-    \r
-    public static final String DOWNLOAD_ADDED_MESSAGE = "DOWNLOAD_ADDED";\r
-    public static final String DOWNLOAD_FINISH_MESSAGE = "DOWNLOAD_FINISH";\r
-    public static final String EXTRA_DOWNLOAD_RESULT = "RESULT";    \r
-    public static final String EXTRA_FILE_PATH = "FILE_PATH";\r
-    public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH";\r
-    public static final String ACCOUNT_NAME = "ACCOUNT_NAME";\r
-    \r
-    private static final String TAG = "FileDownloader";\r
-\r
-    private Looper mServiceLooper;\r
-    private ServiceHandler mServiceHandler;\r
-    private IBinder mBinder;\r
-    private WebdavClient mDownloadClient = null;\r
-    private Account mLastAccount = null;\r
-    private FileDataStorageManager mStorageManager;\r
-    \r
-    private ConcurrentMap<String, DownloadFileOperation> mPendingDownloads = new ConcurrentHashMap<String, DownloadFileOperation>();\r
-    private DownloadFileOperation mCurrentDownload = null;\r
-    \r
-    private NotificationManager mNotificationManager;\r
-    private Notification mNotification;\r
-    private int mLastPercent;\r
-    \r
-    \r
-    /**\r
-     * Builds a key for mPendingDownloads from the account and file to download\r
-     * \r
-     * @param account   Account where the file to download is stored\r
-     * @param file      File to download\r
-     */\r
-    private String buildRemoteName(Account account, OCFile file) {\r
-        return account.name + file.getRemotePath();\r
-    }\r
-\r
-    \r
-    /**\r
-     * Service initialization\r
-     */\r
-    @Override\r
-    public void onCreate() {\r
-        super.onCreate();\r
-        mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);\r
-        HandlerThread thread = new HandlerThread("FileDownloaderThread",\r
-                Process.THREAD_PRIORITY_BACKGROUND);\r
-        thread.start();\r
-        mServiceLooper = thread.getLooper();\r
-        mServiceHandler = new ServiceHandler(mServiceLooper, this);\r
-        mBinder = new FileDownloaderBinder();\r
-    }\r
-\r
-    \r
-    /**\r
-     * Entry point to add one or several files to the queue of downloads.\r
-     * \r
-     * New downloads are added calling to startService(), resulting in a call to this method. This ensures the service will keep on working \r
-     * although the caller activity goes away.\r
-     */\r
-    @Override\r
-    public int onStartCommand(Intent intent, int flags, int startId) {\r
-        if (    !intent.hasExtra(EXTRA_ACCOUNT) ||\r
-                !intent.hasExtra(EXTRA_FILE)\r
-                /*!intent.hasExtra(EXTRA_FILE_PATH) ||\r
-                !intent.hasExtra(EXTRA_REMOTE_PATH)*/\r
-           ) {\r
-            Log.e(TAG, "Not enough information provided in intent");\r
-            return START_NOT_STICKY;\r
-        }\r
-        Account account = intent.getParcelableExtra(EXTRA_ACCOUNT);\r
-        OCFile file = intent.getParcelableExtra(EXTRA_FILE);\r
-        \r
-        AbstractList<String> requestedDownloads = new Vector<String>(); // dvelasco: now this always contains just one element, but that can change in a near future (download of multiple selection)\r
-        String downloadKey = buildRemoteName(account, file);\r
-        try {\r
-            DownloadFileOperation newDownload = new DownloadFileOperation(account, file); \r
-            mPendingDownloads.putIfAbsent(downloadKey, newDownload);\r
-            newDownload.addDatatransferProgressListener(this);\r
-            requestedDownloads.add(downloadKey);\r
-            sendBroadcastNewDownload(newDownload);\r
-            \r
-        } catch (IllegalArgumentException e) {\r
-            Log.e(TAG, "Not enough information provided in intent: " + e.getMessage());\r
-            return START_NOT_STICKY;\r
-        }\r
-        \r
-        if (requestedDownloads.size() > 0) {\r
-            Message msg = mServiceHandler.obtainMessage();\r
-            msg.arg1 = startId;\r
-            msg.obj = requestedDownloads;\r
-            mServiceHandler.sendMessage(msg);\r
-        }\r
-\r
-        return START_NOT_STICKY;\r
-    }\r
-    \r
-    \r
-    /**\r
-     * Provides a binder object that clients can use to perform operations on the queue of downloads, excepting the addition of new files. \r
-     * \r
-     * Implemented to perform cancellation, pause and resume of existing downloads.\r
-     */\r
-    @Override\r
-    public IBinder onBind(Intent arg0) {\r
-        return mBinder;\r
-    }\r
-\r
-    \r
-    /**\r
-     *  Binder to let client components to perform operations on the queue of downloads.\r
-     * \r
-     *  It provides by itself the available operations.\r
-     */\r
-    public class FileDownloaderBinder extends Binder {\r
-        \r
-        /**\r
-         * Cancels a pending or current download of a remote file.\r
-         * \r
-         * @param account       Owncloud account where the remote file is stored.\r
-         * @param file          A file in the queue of pending downloads\r
-         */\r
-        public void cancel(Account account, OCFile file) {\r
-            DownloadFileOperation download = null;\r
-            synchronized (mPendingDownloads) {\r
-                download = mPendingDownloads.remove(buildRemoteName(account, file));\r
-            }\r
-            if (download != null) {\r
-                download.cancel();\r
-            }\r
-        }\r
-        \r
-        \r
-        /**\r
-         * Returns True when the file described by 'file' in the ownCloud account 'account' is downloading or waiting to download.\r
-         * \r
-         * If 'file' is a directory, returns 'true' if some of its descendant files is downloading or waiting to download. \r
-         * \r
-         * @param account       Owncloud account where the remote file is stored.\r
-         * @param file          A file that could be in the queue of downloads.\r
-         */\r
-        public boolean isDownloading(Account account, OCFile file) {\r
-            if (account == null || file == null) return false;\r
-            String targetKey = buildRemoteName(account, file);\r
-            synchronized (mPendingDownloads) {\r
-                if (file.isDirectory()) {\r
-                    // this can be slow if there are many downloads :(\r
-                    Iterator<String> it = mPendingDownloads.keySet().iterator();\r
-                    boolean found = false;\r
-                    while (it.hasNext() && !found) {\r
-                        found = it.next().startsWith(targetKey);\r
-                    }\r
-                    return found;\r
-                } else {\r
-                    return (mPendingDownloads.containsKey(targetKey));\r
-                }\r
-            }\r
-        }\r
-    }\r
-    \r
-    \r
-    /** \r
-     * Download worker. Performs the pending downloads in the order they were requested. \r
-     * \r
-     * Created with the Looper of a new thread, started in {@link FileUploader#onCreate()}. \r
-     */\r
-    private static class ServiceHandler extends Handler {\r
-        // don't make it a final class, and don't remove the static ; lint will warn about a possible memory leak\r
-        FileDownloader mService;\r
-        public ServiceHandler(Looper looper, FileDownloader service) {\r
-            super(looper);\r
-            if (service == null)\r
-                throw new IllegalArgumentException("Received invalid NULL in parameter 'service'");\r
-            mService = service;\r
-        }\r
-\r
-        @Override\r
-        public void handleMessage(Message msg) {\r
-            @SuppressWarnings("unchecked")\r
-            AbstractList<String> requestedDownloads = (AbstractList<String>) msg.obj;\r
-            if (msg.obj != null) {\r
-                Iterator<String> it = requestedDownloads.iterator();\r
-                while (it.hasNext()) {\r
-                    mService.downloadFile(it.next());\r
-                }\r
-            }\r
-            mService.stopSelf(msg.arg1);\r
-        }\r
-    }\r
-    \r
-    \r
-\r
-    /**\r
-     * Core download method: requests a file to download and stores it.\r
-     * \r
-     * @param downloadKey   Key to access the download to perform, contained in mPendingDownloads \r
-     */\r
-    private void downloadFile(String downloadKey) {\r
-        \r
-        synchronized(mPendingDownloads) {\r
-            mCurrentDownload = mPendingDownloads.get(downloadKey);\r
-        }\r
-        \r
-        if (mCurrentDownload != null) {\r
-            \r
-            notifyDownloadStart(mCurrentDownload);\r
-\r
-            RemoteOperationResult downloadResult = null;\r
-            try {\r
-                /// prepare client object to send the request to the ownCloud server\r
-                if (mDownloadClient == null || !mLastAccount.equals(mCurrentDownload.getAccount())) {\r
-                    mLastAccount = mCurrentDownload.getAccount();\r
-                    mStorageManager = new FileDataStorageManager(mLastAccount, getContentResolver());\r
-                    mDownloadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext());\r
-                }\r
-\r
-                /// perform the download\r
-                if (downloadResult == null) {\r
-                    downloadResult = mCurrentDownload.execute(mDownloadClient);\r
-                }\r
-                if (downloadResult.isSuccess()) {\r
-                    saveDownloadedFile();\r
-                }\r
-            \r
-            } catch (AccountsException e) {\r
-                Log.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);\r
-                downloadResult = new RemoteOperationResult(e);\r
-            } catch (IOException e) {\r
-                Log.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);\r
-                downloadResult = new RemoteOperationResult(e);\r
-                \r
-            } finally {\r
-                synchronized(mPendingDownloads) {\r
-                    mPendingDownloads.remove(downloadKey);\r
-                }\r
-            }\r
-\r
-            \r
-            /// notify result\r
-            notifyDownloadResult(mCurrentDownload, downloadResult);\r
-            \r
-            sendBroadcastDownloadFinished(mCurrentDownload, downloadResult);\r
-        }\r
-    }\r
-\r
-\r
-    /**\r
-     * Updates the OC File after a successful download.\r
-     */\r
-    private void saveDownloadedFile() {\r
-        OCFile file = mCurrentDownload.getFile();\r
-        long syncDate = System.currentTimeMillis();\r
-        file.setLastSyncDateForProperties(syncDate);\r
-        file.setLastSyncDateForData(syncDate);\r
-        file.setModificationTimestamp(mCurrentDownload.getModificationTimestamp());\r
-        file.setModificationTimestampAtLastSyncForData(mCurrentDownload.getModificationTimestamp());\r
-        // file.setEtag(mCurrentDownload.getEtag());    // TODO Etag, where available\r
-        file.setMimetype(mCurrentDownload.getMimeType());\r
-        file.setStoragePath(mCurrentDownload.getSavePath());\r
-        file.setFileLength((new File(mCurrentDownload.getSavePath()).length()));\r
-        mStorageManager.saveFile(file);\r
-    }\r
-\r
-\r
-    /**\r
-     * Creates a status notification to show the download progress\r
-     * \r
-     * @param download  Download operation starting.\r
-     */\r
-    private void notifyDownloadStart(DownloadFileOperation download) {\r
-        /// create status notification with a progress bar\r
-        mLastPercent = 0;\r
-        mNotification = new Notification(R.drawable.icon, getString(R.string.downloader_download_in_progress_ticker), System.currentTimeMillis());\r
-        mNotification.flags |= Notification.FLAG_ONGOING_EVENT;\r
-        mNotification.contentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.progressbar_layout);\r
-        mNotification.contentView.setProgressBar(R.id.status_progress, 100, 0, download.getSize() < 0);\r
-        mNotification.contentView.setTextViewText(R.id.status_text, String.format(getString(R.string.downloader_download_in_progress_content), 0, new File(download.getSavePath()).getName()));\r
-        mNotification.contentView.setImageViewResource(R.id.status_icon, R.drawable.icon);\r
-        \r
-        /// includes a pending intent in the notification showing the details view of the file\r
-        Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);\r
-        showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, download.getFile());\r
-        showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, download.getAccount());\r
-        showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);\r
-        mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), showDetailsIntent, 0);\r
-        \r
-        mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotification);\r
-    }\r
-\r
-    \r
-    /**\r
-     * Callback method to update the progress bar in the status notification.\r
-     */\r
-    @Override\r
-    public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String fileName) {\r
-        int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer));\r
-        if (percent != mLastPercent) {\r
-          mNotification.contentView.setProgressBar(R.id.status_progress, 100, percent, totalToTransfer < 0);\r
-          String text = String.format(getString(R.string.downloader_download_in_progress_content), percent, fileName);\r
-          mNotification.contentView.setTextViewText(R.id.status_text, text);\r
-          mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotification);\r
-        }\r
-        mLastPercent = percent;\r
-    }\r
-    \r
-    \r
-    /**\r
-     * Callback method to update the progress bar in the status notification (old version)\r
-     */\r
-    @Override\r
-    public void onTransferProgress(long progressRate) {\r
-        // NOTHING TO DO HERE ANYMORE\r
-    }\r
-    \r
-\r
-    /**\r
-     * Updates the status notification with the result of a download operation.\r
-     * \r
-     * @param downloadResult    Result of the download operation.\r
-     * @param download          Finished download operation\r
-     */\r
-    private void notifyDownloadResult(DownloadFileOperation download, RemoteOperationResult downloadResult) {\r
-        mNotificationManager.cancel(R.string.downloader_download_in_progress_ticker);\r
-        if (!downloadResult.isCancelled()) {\r
-            int tickerId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_ticker : R.string.downloader_download_failed_ticker;\r
-            int contentId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_content : R.string.downloader_download_failed_content;\r
-            Notification finalNotification = new Notification(R.drawable.icon, getString(tickerId), System.currentTimeMillis());\r
-            finalNotification.flags |= Notification.FLAG_AUTO_CANCEL;\r
-            boolean needsToUpdateCredentials = (downloadResult.getCode() == ResultCode.UNAUTHORIZED);\r
-            if (needsToUpdateCredentials) {\r
-                // let the user update credentials with one click\r
-                Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);\r
-                updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, download.getAccount());\r
-                updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_TOKEN);\r
-                updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\r
-                updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);\r
-                updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND);\r
-                finalNotification.contentIntent = PendingIntent.getActivity(this, (int)System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT);\r
-                finalNotification.setLatestEventInfo(   getApplicationContext(), \r
-                                                        getString(tickerId), \r
-                                                        String.format(getString(contentId), new File(download.getSavePath()).getName()),\r
-                                                        finalNotification.contentIntent);\r
-                mDownloadClient = null;   // grant that future retries on the same account will get the fresh credentials\r
-                \r
-            } else {\r
-                // TODO put something smart in the contentIntent below\r
-                finalNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);\r
-                finalNotification.setLatestEventInfo(getApplicationContext(), getString(tickerId), String.format(getString(contentId), new File(download.getSavePath()).getName()), finalNotification.contentIntent);\r
-            }\r
-            mNotificationManager.notify(tickerId, finalNotification);\r
-        }\r
-    }\r
-    \r
-    \r
-    /**\r
-     * Sends a broadcast when a download finishes in order to the interested activities can update their view\r
-     * \r
-     * @param download          Finished download operation\r
-     * @param downloadResult    Result of the download operation\r
-     */\r
-    private void sendBroadcastDownloadFinished(DownloadFileOperation download, RemoteOperationResult downloadResult) {\r
-        Intent end = new Intent(DOWNLOAD_FINISH_MESSAGE);\r
-        end.putExtra(EXTRA_DOWNLOAD_RESULT, downloadResult.isSuccess());\r
-        end.putExtra(ACCOUNT_NAME, download.getAccount().name);\r
-        end.putExtra(EXTRA_REMOTE_PATH, download.getRemotePath());\r
-        end.putExtra(EXTRA_FILE_PATH, download.getSavePath());\r
-        sendStickyBroadcast(end);\r
-    }\r
-    \r
-    \r
-    /**\r
-     * Sends a broadcast when a new download is added to the queue.\r
-     * \r
-     * @param download          Added download operation\r
-     */\r
-    private void sendBroadcastNewDownload(DownloadFileOperation download) {\r
-        Intent added = new Intent(DOWNLOAD_ADDED_MESSAGE);\r
-        /*added.putExtra(ACCOUNT_NAME, download.getAccount().name);\r
-        added.putExtra(EXTRA_REMOTE_PATH, download.getRemotePath());*/\r
-        added.putExtra(EXTRA_FILE_PATH, download.getSavePath());\r
-        sendStickyBroadcast(added);\r
-    }\r
-\r
-}\r
+/* ownCloud Android client application
+ *   Copyright (C) 2012 Bartek Przybylski
+ *   Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License 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.files.services;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.AbstractList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Vector;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.owncloud.android.authentication.AuthenticatorActivity;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import eu.alefzero.webdav.OnDatatransferProgressListener;
+
+import com.owncloud.android.network.OwnCloudClientUtils;
+import com.owncloud.android.operations.DownloadFileOperation;
+import com.owncloud.android.operations.RemoteOperationResult;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.ui.activity.FileDetailActivity;
+import com.owncloud.android.ui.fragment.FileDetailFragment;
+import com.owncloud.android.ui.preview.PreviewImageActivity;
+import com.owncloud.android.ui.preview.PreviewImageFragment;
+
+import android.accounts.Account;
+import android.accounts.AccountsException;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.widget.RemoteViews;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import eu.alefzero.webdav.WebdavClient;
+
+public class FileDownloader extends Service implements OnDatatransferProgressListener {
+    
+    public static final String EXTRA_ACCOUNT = "ACCOUNT";
+    public static final String EXTRA_FILE = "FILE";
+    
+    public static final String DOWNLOAD_ADDED_MESSAGE = "DOWNLOAD_ADDED";
+    public static final String DOWNLOAD_FINISH_MESSAGE = "DOWNLOAD_FINISH";
+    public static final String EXTRA_DOWNLOAD_RESULT = "RESULT";    
+    public static final String EXTRA_FILE_PATH = "FILE_PATH";
+    public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH";
+    public static final String ACCOUNT_NAME = "ACCOUNT_NAME";
+    
+    private static final String TAG = "FileDownloader";
+
+    private Looper mServiceLooper;
+    private ServiceHandler mServiceHandler;
+    private IBinder mBinder;
+    private WebdavClient mDownloadClient = null;
+    private Account mLastAccount = null;
+    private FileDataStorageManager mStorageManager;
+    
+    private ConcurrentMap<String, DownloadFileOperation> mPendingDownloads = new ConcurrentHashMap<String, DownloadFileOperation>();
+    private DownloadFileOperation mCurrentDownload = null;
+    
+    private NotificationManager mNotificationManager;
+    private Notification mNotification;
+    private int mLastPercent;
+    
+    
+    /**
+     * Builds a key for mPendingDownloads from the account and file to download
+     * 
+     * @param account   Account where the file to download is stored
+     * @param file      File to download
+     */
+    private String buildRemoteName(Account account, OCFile file) {
+        return account.name + file.getRemotePath();
+    }
+
+    
+    /**
+     * Service initialization
+     */
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+        HandlerThread thread = new HandlerThread("FileDownloaderThread",
+                Process.THREAD_PRIORITY_BACKGROUND);
+        thread.start();
+        mServiceLooper = thread.getLooper();
+        mServiceHandler = new ServiceHandler(mServiceLooper, this);
+        mBinder = new FileDownloaderBinder();
+    }
+
+    /**
+     * Entry point to add one or several files to the queue of downloads.
+     * 
+     * New downloads are added calling to startService(), resulting in a call to this method. This ensures the service will keep on working 
+     * although the caller activity goes away.
+     */
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        if (    !intent.hasExtra(EXTRA_ACCOUNT) ||
+                !intent.hasExtra(EXTRA_FILE)
+                /*!intent.hasExtra(EXTRA_FILE_PATH) ||
+                !intent.hasExtra(EXTRA_REMOTE_PATH)*/
+           ) {
+            Log_OC.e(TAG, "Not enough information provided in intent");
+            return START_NOT_STICKY;
+        }
+        Account account = intent.getParcelableExtra(EXTRA_ACCOUNT);
+        OCFile file = intent.getParcelableExtra(EXTRA_FILE);
+        
+        AbstractList<String> requestedDownloads = new Vector<String>(); // dvelasco: now this always contains just one element, but that can change in a near future (download of multiple selection)
+        String downloadKey = buildRemoteName(account, file);
+        try {
+            DownloadFileOperation newDownload = new DownloadFileOperation(account, file); 
+            mPendingDownloads.putIfAbsent(downloadKey, newDownload);
+            newDownload.addDatatransferProgressListener(this);
+            newDownload.addDatatransferProgressListener((FileDownloaderBinder)mBinder);
+            requestedDownloads.add(downloadKey);
+            sendBroadcastNewDownload(newDownload);
+            
+        } catch (IllegalArgumentException e) {
+            Log_OC.e(TAG, "Not enough information provided in intent: " + e.getMessage());
+            return START_NOT_STICKY;
+        }
+        
+        if (requestedDownloads.size() > 0) {
+            Message msg = mServiceHandler.obtainMessage();
+            msg.arg1 = startId;
+            msg.obj = requestedDownloads;
+            mServiceHandler.sendMessage(msg);
+        }
+
+        return START_NOT_STICKY;
+    }
+    
+    
+    /**
+     * Provides a binder object that clients can use to perform operations on the queue of downloads, excepting the addition of new files. 
+     * 
+     * Implemented to perform cancellation, pause and resume of existing downloads.
+     */
+    @Override
+    public IBinder onBind(Intent arg0) {
+        return mBinder;
+    }
+
+
+    /**
+     * Called when ALL the bound clients were onbound.
+     */
+    @Override
+    public boolean onUnbind(Intent intent) {
+        ((FileDownloaderBinder)mBinder).clearListeners();
+        return false;   // not accepting rebinding (default behaviour)
+    }
+
+    
+    /**
+     *  Binder to let client components to perform operations on the queue of downloads.
+     * 
+     *  It provides by itself the available operations.
+     */
+    public class FileDownloaderBinder extends Binder implements OnDatatransferProgressListener {
+        
+        /** 
+         * Map of listeners that will be reported about progress of downloads from a {@link FileDownloaderBinder} instance 
+         */
+        private Map<String, OnDatatransferProgressListener> mBoundListeners = new HashMap<String, OnDatatransferProgressListener>();
+        
+        
+        /**
+         * Cancels a pending or current download of a remote file.
+         * 
+         * @param account       Owncloud account where the remote file is stored.
+         * @param file          A file in the queue of pending downloads
+         */
+        public void cancel(Account account, OCFile file) {
+            DownloadFileOperation download = null;
+            synchronized (mPendingDownloads) {
+                download = mPendingDownloads.remove(buildRemoteName(account, file));
+            }
+            if (download != null) {
+                download.cancel();
+            }
+        }
+        
+        
+        public void clearListeners() {
+            mBoundListeners.clear();
+        }
+
+
+        /**
+         * Returns True when the file described by 'file' in the ownCloud account 'account' is downloading or waiting to download.
+         * 
+         * If 'file' is a directory, returns 'true' if some of its descendant files is downloading or waiting to download. 
+         * 
+         * @param account       Owncloud account where the remote file is stored.
+         * @param file          A file that could be in the queue of downloads.
+         */
+        public boolean isDownloading(Account account, OCFile file) {
+            if (account == null || file == null) return false;
+            String targetKey = buildRemoteName(account, file);
+            synchronized (mPendingDownloads) {
+                if (file.isDirectory()) {
+                    // this can be slow if there are many downloads :(
+                    Iterator<String> it = mPendingDownloads.keySet().iterator();
+                    boolean found = false;
+                    while (it.hasNext() && !found) {
+                        found = it.next().startsWith(targetKey);
+                    }
+                    return found;
+                } else {
+                    return (mPendingDownloads.containsKey(targetKey));
+                }
+            }
+        }
+
+        
+        /**
+         * Adds a listener interested in the progress of the download for a concrete file.
+         * 
+         * @param listener      Object to notify about progress of transfer.    
+         * @param account       ownCloud account holding the file of interest.
+         * @param file          {@link OCfile} of interest for listener. 
+         */
+        public void addDatatransferProgressListener (OnDatatransferProgressListener listener, Account account, OCFile file) {
+            if (account == null || file == null || listener == null) return;
+            String targetKey = buildRemoteName(account, file);
+            mBoundListeners.put(targetKey, listener);
+        }
+        
+        
+        /**
+         * Removes a listener interested in the progress of the download for a concrete file.
+         * 
+         * @param listener      Object to notify about progress of transfer.    
+         * @param account       ownCloud account holding the file of interest.
+         * @param file          {@link OCfile} of interest for listener. 
+         */
+        public void removeDatatransferProgressListener (OnDatatransferProgressListener listener, Account account, OCFile file) {
+            if (account == null || file == null || listener == null) return;
+            String targetKey = buildRemoteName(account, file);
+            if (mBoundListeners.get(targetKey) == listener) {
+                mBoundListeners.remove(targetKey);
+            }
+        }
+
+
+        @Override
+        public void onTransferProgress(long progressRate) {
+            // old way, should not be in use any more
+        }
+
+
+        @Override
+        public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer,
+                String fileName) {
+            String key = buildRemoteName(mCurrentDownload.getAccount(), mCurrentDownload.getFile());
+            OnDatatransferProgressListener boundListener = mBoundListeners.get(key);
+            if (boundListener != null) {
+                boundListener.onTransferProgress(progressRate, totalTransferredSoFar, totalToTransfer, fileName);
+            }
+        }
+        
+    }
+    
+    
+    /** 
+     * Download worker. Performs the pending downloads in the order they were requested. 
+     * 
+     * Created with the Looper of a new thread, started in {@link FileUploader#onCreate()}. 
+     */
+    private static class ServiceHandler extends Handler {
+        // don't make it a final class, and don't remove the static ; lint will warn about a possible memory leak
+        FileDownloader mService;
+        public ServiceHandler(Looper looper, FileDownloader service) {
+            super(looper);
+            if (service == null)
+                throw new IllegalArgumentException("Received invalid NULL in parameter 'service'");
+            mService = service;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            @SuppressWarnings("unchecked")
+            AbstractList<String> requestedDownloads = (AbstractList<String>) msg.obj;
+            if (msg.obj != null) {
+                Iterator<String> it = requestedDownloads.iterator();
+                while (it.hasNext()) {
+                    mService.downloadFile(it.next());
+                }
+            }
+            mService.stopSelf(msg.arg1);
+        }
+    }
+    
+
+    /**
+     * Core download method: requests a file to download and stores it.
+     * 
+     * @param downloadKey   Key to access the download to perform, contained in mPendingDownloads 
+     */
+    private void downloadFile(String downloadKey) {
+        
+        synchronized(mPendingDownloads) {
+            mCurrentDownload = mPendingDownloads.get(downloadKey);
+        }
+        
+        if (mCurrentDownload != null) {
+            
+            notifyDownloadStart(mCurrentDownload);
+
+            RemoteOperationResult downloadResult = null;
+            try {
+                /// prepare client object to send the request to the ownCloud server
+                if (mDownloadClient == null || !mLastAccount.equals(mCurrentDownload.getAccount())) {
+                    mLastAccount = mCurrentDownload.getAccount();
+                    mStorageManager = new FileDataStorageManager(mLastAccount, getContentResolver());
+                    mDownloadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext());
+                }
+
+                /// perform the download
+                downloadResult = mCurrentDownload.execute(mDownloadClient);
+                if (downloadResult.isSuccess()) {
+                    saveDownloadedFile();
+                }
+            
+            } catch (AccountsException e) {
+                Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
+                downloadResult = new RemoteOperationResult(e);
+            } catch (IOException e) {
+                Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
+                downloadResult = new RemoteOperationResult(e);
+                
+            } finally {
+                synchronized(mPendingDownloads) {
+                    mPendingDownloads.remove(downloadKey);
+                }
+            }
+
+            
+            /// notify result
+            notifyDownloadResult(mCurrentDownload, downloadResult);
+            
+            sendBroadcastDownloadFinished(mCurrentDownload, downloadResult);
+        }
+    }
+
+
+    /**
+     * Updates the OC File after a successful download.
+     */
+    private void saveDownloadedFile() {
+        OCFile file = mCurrentDownload.getFile();
+        long syncDate = System.currentTimeMillis();
+        file.setLastSyncDateForProperties(syncDate);
+        file.setLastSyncDateForData(syncDate);
+        file.setModificationTimestamp(mCurrentDownload.getModificationTimestamp());
+        file.setModificationTimestampAtLastSyncForData(mCurrentDownload.getModificationTimestamp());
+        // file.setEtag(mCurrentDownload.getEtag());    // TODO Etag, where available
+        file.setMimetype(mCurrentDownload.getMimeType());
+        file.setStoragePath(mCurrentDownload.getSavePath());
+        file.setFileLength((new File(mCurrentDownload.getSavePath()).length()));
+        mStorageManager.saveFile(file);
+    }
+
+
+    /**
+     * Creates a status notification to show the download progress
+     * 
+     * @param download  Download operation starting.
+     */
+    private void notifyDownloadStart(DownloadFileOperation download) {
+        /// create status notification with a progress bar
+        mLastPercent = 0;
+        mNotification = new Notification(R.drawable.icon, getString(R.string.downloader_download_in_progress_ticker), System.currentTimeMillis());
+        mNotification.flags |= Notification.FLAG_ONGOING_EVENT;
+        mNotification.contentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.progressbar_layout);
+        mNotification.contentView.setProgressBar(R.id.status_progress, 100, 0, download.getSize() < 0);
+        mNotification.contentView.setTextViewText(R.id.status_text, String.format(getString(R.string.downloader_download_in_progress_content), 0, new File(download.getSavePath()).getName()));
+        mNotification.contentView.setImageViewResource(R.id.status_icon, R.drawable.icon);
+        
+        /// includes a pending intent in the notification showing the details view of the file
+        Intent showDetailsIntent = null;
+        if (PreviewImageFragment.canBePreviewed(download.getFile())) {
+            showDetailsIntent = new Intent(this, PreviewImageActivity.class);
+        } else {
+            showDetailsIntent = new Intent(this, FileDetailActivity.class);
+        }
+        showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, download.getFile());
+        showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, download.getAccount());
+        showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), showDetailsIntent, 0);
+        
+        mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotification);
+    }
+
+    
+    /**
+     * Callback method to update the progress bar in the status notification.
+     */
+    @Override
+    public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String fileName) {
+        int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer));
+        if (percent != mLastPercent) {
+          mNotification.contentView.setProgressBar(R.id.status_progress, 100, percent, totalToTransfer < 0);
+          String text = String.format(getString(R.string.downloader_download_in_progress_content), percent, fileName);
+          mNotification.contentView.setTextViewText(R.id.status_text, text);
+          mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotification);
+        }
+        mLastPercent = percent;
+    }
+    
+    
+    /**
+     * Callback method to update the progress bar in the status notification (old version)
+     */
+    @Override
+    public void onTransferProgress(long progressRate) {
+        // NOTHING TO DO HERE ANYMORE
+    }
+    
+
+    /**
+     * Updates the status notification with the result of a download operation.
+     * 
+     * @param downloadResult    Result of the download operation.
+     * @param download          Finished download operation
+     */
+    private void notifyDownloadResult(DownloadFileOperation download, RemoteOperationResult downloadResult) {
+        mNotificationManager.cancel(R.string.downloader_download_in_progress_ticker);
+        if (!downloadResult.isCancelled()) {
+            int tickerId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_ticker : R.string.downloader_download_failed_ticker;
+            int contentId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_content : R.string.downloader_download_failed_content;
+            Notification finalNotification = new Notification(R.drawable.icon, getString(tickerId), System.currentTimeMillis());
+            finalNotification.flags |= Notification.FLAG_AUTO_CANCEL;
+            boolean needsToUpdateCredentials = (downloadResult.getCode() == ResultCode.UNAUTHORIZED);
+            if (needsToUpdateCredentials) {
+                // let the user update credentials with one click
+                Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
+                updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, download.getAccount());
+                updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_TOKEN);
+                updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND);
+                finalNotification.contentIntent = PendingIntent.getActivity(this, (int)System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT);
+                finalNotification.setLatestEventInfo(   getApplicationContext(), 
+                                                        getString(tickerId), 
+                                                        String.format(getString(contentId), new File(download.getSavePath()).getName()),
+                                                        finalNotification.contentIntent);
+                mDownloadClient = null;   // grant that future retries on the same account will get the fresh credentials
+                
+            } else {
+                Intent showDetailsIntent = null;
+                if (downloadResult.isSuccess()) {
+                    if (PreviewImageFragment.canBePreviewed(download.getFile())) {
+                        showDetailsIntent = new Intent(this, PreviewImageActivity.class);
+                    } else {
+                        showDetailsIntent = new Intent(this, FileDetailActivity.class);
+                    }
+                    showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, download.getFile());
+                    showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, download.getAccount());
+                    showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+                    
+                } else {
+                    // TODO put something smart in showDetailsIntent
+                    showDetailsIntent = new Intent();
+                }
+                finalNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), showDetailsIntent, 0);
+                finalNotification.setLatestEventInfo(getApplicationContext(), getString(tickerId), String.format(getString(contentId), new File(download.getSavePath()).getName()), finalNotification.contentIntent);
+            }
+            mNotificationManager.notify(tickerId, finalNotification);
+        }
+    }
+    
+    
+    /**
+     * Sends a broadcast when a download finishes in order to the interested activities can update their view
+     * 
+     * @param download          Finished download operation
+     * @param downloadResult    Result of the download operation
+     */
+    private void sendBroadcastDownloadFinished(DownloadFileOperation download, RemoteOperationResult downloadResult) {
+        Intent end = new Intent(DOWNLOAD_FINISH_MESSAGE);
+        end.putExtra(EXTRA_DOWNLOAD_RESULT, downloadResult.isSuccess());
+        end.putExtra(ACCOUNT_NAME, download.getAccount().name);
+        end.putExtra(EXTRA_REMOTE_PATH, download.getRemotePath());
+        end.putExtra(EXTRA_FILE_PATH, download.getSavePath());
+        sendStickyBroadcast(end);
+    }
+    
+    
+    /**
+     * Sends a broadcast when a new download is added to the queue.
+     * 
+     * @param download          Added download operation
+     */
+    private void sendBroadcastNewDownload(DownloadFileOperation download) {
+        Intent added = new Intent(DOWNLOAD_ADDED_MESSAGE);
+        added.putExtra(ACCOUNT_NAME, download.getAccount().name);
+        added.putExtra(EXTRA_REMOTE_PATH, download.getRemotePath());
+        added.putExtra(EXTRA_FILE_PATH, download.getSavePath());
+        sendStickyBroadcast(added);
+    }
+
+}
index d4bf24d..fd1412c 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -23,6 +22,7 @@ import java.io.File;
 import java.util.HashMap;
 import java.util.Map;
 
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
@@ -40,7 +40,6 @@ import android.content.IntentFilter;
 import android.database.Cursor;
 import android.os.Binder;
 import android.os.IBinder;
-import android.util.Log;
 
 public class FileObserverService extends Service {
 
@@ -83,7 +82,7 @@ public class FileObserverService extends Service {
         super.onDestroy();
         unregisterReceiver(mDownloadReceiver);
         mObserversMap = null;   // TODO study carefully the life cycle of Services to grant the best possible observance
-        Log.d(TAG, "Bye, bye");
+        Log_OC.d(TAG, "Bye, bye");
     }
     
     
@@ -102,7 +101,7 @@ public class FileObserverService extends Service {
         }
             
         if (!intent.hasExtra(KEY_FILE_CMD)) {
-            Log.e(TAG, "No KEY_FILE_CMD argument given");
+            Log_OC.e(TAG, "No KEY_FILE_CMD argument given");
             return Service.START_STICKY;
         }
 
@@ -119,7 +118,7 @@ public class FileObserverService extends Service {
                                     (Account)intent.getParcelableExtra(KEY_CMD_ARG_ACCOUNT));
                 break;
             default:
-                Log.wtf(TAG, "Incorrect key given");
+                Log_OC.wtf(TAG, "Incorrect key given");
         }
 
         return Service.START_STICKY;
@@ -166,7 +165,7 @@ public class FileObserverService extends Service {
             mObserversMap.put(path, observer);
             if (new File(path).exists()) {
                 observer.startWatching();
-                Log.d(TAG, "Started watching file " + path);
+                Log_OC.d(TAG, "Started watching file " + path);
             }
             
         } while (c.moveToNext());
@@ -189,7 +188,7 @@ public class FileObserverService extends Service {
      */
     private void addObservedFile(OCFile file, Account account) {
         if (file == null) {
-            Log.e(TAG, "Trying to add a NULL file to observer");
+            Log_OC.e(TAG, "Trying to add a NULL file to observer");
             return;
         }
         String localPath = file.getStoragePath();
@@ -204,11 +203,11 @@ public class FileObserverService extends Service {
                                                     getApplicationContext(), 
                                                     OwnCloudFileObserver.CHANGES_ONLY);
             mObserversMap.put(localPath, observer);
-            Log.d(TAG, "Observer added for path " + localPath);
+            Log_OC.d(TAG, "Observer added for path " + localPath);
         
             if (file.isDown()) {
                 observer.startWatching();
-                Log.d(TAG, "Started watching " + localPath);
+                Log_OC.d(TAG, "Started watching " + localPath);
             }   // else - the observance can't be started on a file not already down; mDownloadReceiver will get noticed when the download of the file finishes
         }
         
@@ -229,7 +228,7 @@ public class FileObserverService extends Service {
      */
     private void removeObservedFile(OCFile file, Account account) {
         if (file == null) {
-            Log.e(TAG, "Trying to remove a NULL file");
+            Log_OC.e(TAG, "Trying to remove a NULL file");
             return;
         }
         String localPath = file.getStoragePath();
@@ -241,7 +240,7 @@ public class FileObserverService extends Service {
         if (observer != null) {
             observer.stopWatching();
             mObserversMap.remove(observer);
-            Log.d(TAG, "Stopped watching " + localPath);
+            Log_OC.d(TAG, "Stopped watching " + localPath);
         }
         
     }
@@ -261,13 +260,13 @@ public class FileObserverService extends Service {
             OwnCloudFileObserver observer = mObserversMap.get(downloadPath);
             if (observer != null) {
                 if (intent.getAction().equals(FileDownloader.DOWNLOAD_FINISH_MESSAGE) &&
-                        new File(downloadPath).exists()) {  // the download could be successful, or not; in both cases, the file could be down, due to a former download or upload   
+                        new File(downloadPath).exists()) {  // the download could be successful. not; in both cases, the file could be down, due to a former download or upload   
                     observer.startWatching();
-                    Log.d(TAG, "Watching again " + downloadPath);
+                    Log_OC.d(TAG, "Watching again " + downloadPath);
                 
                 } else if (intent.getAction().equals(FileDownloader.DOWNLOAD_ADDED_MESSAGE)) {
                     observer.stopWatching();
-                    Log.d(TAG, "Disabling observance of " + downloadPath);
+                    Log_OC.d(TAG, "Disabling observance of " + downloadPath);
                 } 
             }
         }
index 04044e3..7866098 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -22,7 +21,9 @@ package com.owncloud.android.files.services;
 import java.io.File;
 import java.io.IOException;
 import java.util.AbstractList;
+import java.util.HashMap;
 import java.util.Iterator;
+import java.util.Map;
 import java.util.Vector;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
@@ -35,7 +36,6 @@ import com.owncloud.android.authentication.AccountAuthenticator;
 import com.owncloud.android.authentication.AuthenticatorActivity;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.files.InstantUploadBroadcastReceiver;
 import com.owncloud.android.operations.ChunkedUploadFileOperation;
 import com.owncloud.android.operations.CreateFolderOperation;
 import com.owncloud.android.operations.RemoteOperation;
@@ -67,11 +67,18 @@ import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Process;
-import android.util.Log;
 import android.webkit.MimeTypeMap;
 import android.widget.RemoteViews;
 
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.R;
+import com.owncloud.android.db.DbHandler;
+import com.owncloud.android.ui.activity.FailedUploadActivity;
+import com.owncloud.android.ui.activity.InstantUploadActivity;
+import com.owncloud.android.ui.preview.PreviewImageActivity;
+import com.owncloud.android.ui.preview.PreviewImageFragment;
+import com.owncloud.android.utils.FileStorageUtils;
+
 import eu.alefzero.webdav.WebdavClient;
 
 public class FileUploader extends Service implements OnDatatransferProgressListener {
@@ -81,20 +88,20 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
     public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH";
     public static final String EXTRA_OLD_REMOTE_PATH = "OLD_REMOTE_PATH";
     public static final String EXTRA_OLD_FILE_PATH = "OLD_FILE_PATH";
-    public static final String ACCOUNT_NAME = "ACCOUNT_NAME";    
-    
+    public static final String ACCOUNT_NAME = "ACCOUNT_NAME";
+
     public static final String KEY_FILE = "FILE";
     public static final String KEY_LOCAL_FILE = "LOCAL_FILE";
     public static final String KEY_REMOTE_FILE = "REMOTE_FILE";
     public static final String KEY_MIME_TYPE = "MIME_TYPE";
 
     public static final String KEY_ACCOUNT = "ACCOUNT";
-    
+
     public static final String KEY_UPLOAD_TYPE = "UPLOAD_TYPE";
     public static final String KEY_FORCE_OVERWRITE = "KEY_FORCE_OVERWRITE";
     public static final String KEY_INSTANT_UPLOAD = "INSTANT_UPLOAD";
     public static final String KEY_LOCAL_BEHAVIOUR = "BEHAVIOUR";
-    
+
     public static final int LOCAL_BEHAVIOUR_COPY = 0;
     public static final int LOCAL_BEHAVIOUR_MOVE = 1;
     public static final int LOCAL_BEHAVIOUR_FORGET = 2;
@@ -103,7 +110,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
     public static final int UPLOAD_MULTIPLE_FILES = 1;
 
     private static final String TAG = FileUploader.class.getSimpleName();
-    
+
     private Looper mServiceLooper;
     private ServiceHandler mServiceHandler;
     private IBinder mBinder;
@@ -113,18 +120,17 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
 
     private ConcurrentMap<String, UploadFileOperation> mPendingUploads = new ConcurrentHashMap<String, UploadFileOperation>();
     private UploadFileOperation mCurrentUpload = null;
-    
+
     private NotificationManager mNotificationManager;
     private Notification mNotification;
     private int mLastPercent;
     private RemoteViews mDefaultNotificationContentView;
-    
-    
+
     /**
      * Builds a key for mPendingUploads from the account and file to upload
      * 
-     * @param account   Account where the file to download is stored
-     * @param file      File to download
+     * @param account   Account where the file to upload is stored
+     * @param file      File to upload
      */
     private String buildRemoteName(Account account, OCFile file) {
         return account.name + file.getRemotePath();
@@ -134,72 +140,77 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
         return account.name + remotePath;
     }
 
-    
     /**
      * Checks if an ownCloud server version should support chunked uploads.
      * 
-     * @param version   OwnCloud version instance corresponding to an ownCloud server.
-     * @return          'True' if the ownCloud server with version supports chunked uploads.
+     * @param version OwnCloud version instance corresponding to an ownCloud
+     *            server.
+     * @return 'True' if the ownCloud server with version supports chunked
+     *         uploads.
      */
     private static boolean chunkedUploadIsSupported(OwnCloudVersion version) {
         return (version != null && version.compareTo(OwnCloudVersion.owncloud_v4_5) >= 0);
     }
 
-    
-
     /**
      * Service initialization
      */
     @Override
     public void onCreate() {
         super.onCreate();
+        Log_OC.i(TAG, "mPendingUploads size:" + mPendingUploads.size());
         mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
-        HandlerThread thread = new HandlerThread("FileUploaderThread",
-                Process.THREAD_PRIORITY_BACKGROUND);
+        HandlerThread thread = new HandlerThread("FileUploaderThread", Process.THREAD_PRIORITY_BACKGROUND);
         thread.start();
         mServiceLooper = thread.getLooper();
         mServiceHandler = new ServiceHandler(mServiceLooper, this);
         mBinder = new FileUploaderBinder();
     }
 
-    
     /**
      * Entry point to add one or several files to the queue of uploads.
      * 
-     * New uploads are added calling to startService(), resulting in a call to this method. This ensures the service will keep on working 
-     * although the caller activity goes away.
+     * New uploads are added calling to startService(), resulting in a call to
+     * this method. This ensures the service will keep on working although the
+     * caller activity goes away.
      */
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
-        if (!intent.hasExtra(KEY_ACCOUNT) || !intent.hasExtra(KEY_UPLOAD_TYPE) || !(intent.hasExtra(KEY_LOCAL_FILE) || intent.hasExtra(KEY_FILE))) {
-            Log.e(TAG, "Not enough information provided in intent");
+        if (!intent.hasExtra(KEY_ACCOUNT) || !intent.hasExtra(KEY_UPLOAD_TYPE)
+                || !(intent.hasExtra(KEY_LOCAL_FILE) || intent.hasExtra(KEY_FILE))) {
+            Log_OC.e(TAG, "Not enough information provided in intent");
             return Service.START_NOT_STICKY;
         }
         int uploadType = intent.getIntExtra(KEY_UPLOAD_TYPE, -1);
         if (uploadType == -1) {
-            Log.e(TAG, "Incorrect upload type provided");
+            Log_OC.e(TAG, "Incorrect upload type provided");
             return Service.START_NOT_STICKY;
         }
         Account account = intent.getParcelableExtra(KEY_ACCOUNT);
-        
+
         String[] localPaths = null, remotePaths = null, mimeTypes = null;
         OCFile[] files = null;
         if (uploadType == UPLOAD_SINGLE_FILE) {
-            
+
             if (intent.hasExtra(KEY_FILE)) {
-                files = new OCFile[] {intent.getParcelableExtra(KEY_FILE) };
-                
+                files = new OCFile[] { intent.getParcelableExtra(KEY_FILE) };
+
             } else {
                 localPaths = new String[] { intent.getStringExtra(KEY_LOCAL_FILE) };
                 remotePaths = new String[] { intent.getStringExtra(KEY_REMOTE_FILE) };
                 mimeTypes = new String[] { intent.getStringExtra(KEY_MIME_TYPE) };
             }
-            
+
         } else { // mUploadType == UPLOAD_MULTIPLE_FILES
-            
+
             if (intent.hasExtra(KEY_FILE)) {
-                files = (OCFile[]) intent.getParcelableArrayExtra(KEY_FILE);    // TODO will this casting work fine?
-                
+                files = (OCFile[]) intent.getParcelableArrayExtra(KEY_FILE); // TODO
+                                                                             // will
+                                                                             // this
+                                                                             // casting
+                                                                             // work
+                                                                             // fine?
+
             } else {
                 localPaths = intent.getStringArrayExtra(KEY_LOCAL_FILE);
                 remotePaths = intent.getStringArrayExtra(KEY_REMOTE_FILE);
@@ -208,107 +219,137 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
         }
 
         FileDataStorageManager storageManager = new FileDataStorageManager(account, getContentResolver());
-        
+
         boolean forceOverwrite = intent.getBooleanExtra(KEY_FORCE_OVERWRITE, false);
         boolean isInstant = intent.getBooleanExtra(KEY_INSTANT_UPLOAD, false);
         int localAction = intent.getIntExtra(KEY_LOCAL_BEHAVIOUR, LOCAL_BEHAVIOUR_COPY);
         boolean fixed = false;
         if (isInstant) {
-            fixed = checkAndFixInstantUploadDirectory(storageManager);  // MUST be done BEFORE calling obtainNewOCFileToUpload
+            fixed = checkAndFixInstantUploadDirectory(storageManager); // MUST
+                                                                       // be
+                                                                       // done
+                                                                       // BEFORE
+                                                                       // calling
+                                                                       // obtainNewOCFileToUpload
         }
-        
+
         if (intent.hasExtra(KEY_FILE) && files == null) {
-            Log.e(TAG, "Incorrect array for OCFiles provided in upload intent");
+            Log_OC.e(TAG, "Incorrect array for OCFiles provided in upload intent");
             return Service.START_NOT_STICKY;
-            
+
         } else if (!intent.hasExtra(KEY_FILE)) {
             if (localPaths == null) {
-                Log.e(TAG, "Incorrect array for local paths provided in upload intent");
+                Log_OC.e(TAG, "Incorrect array for local paths provided in upload intent");
                 return Service.START_NOT_STICKY;
             }
             if (remotePaths == null) {
-                Log.e(TAG, "Incorrect array for remote paths provided in upload intent");
+                Log_OC.e(TAG, "Incorrect array for remote paths provided in upload intent");
                 return Service.START_NOT_STICKY;
             }
             if (localPaths.length != remotePaths.length) {
-                Log.e(TAG, "Different number of remote paths and local paths!");
+                Log_OC.e(TAG, "Different number of remote paths and local paths!");
                 return Service.START_NOT_STICKY;
             }
-            
+
             files = new OCFile[localPaths.length];
-            for (int i=0; i < localPaths.length; i++) {
-                files[i] = obtainNewOCFileToUpload(remotePaths[i], localPaths[i], ((mimeTypes!=null)?mimeTypes[i]:(String)null), storageManager);
+            for (int i = 0; i < localPaths.length; i++) {
+                files[i] = obtainNewOCFileToUpload(remotePaths[i], localPaths[i], ((mimeTypes != null) ? mimeTypes[i]
+                        : (String) null), storageManager);
+                if (files[i] == null) {
+                    // TODO @andomaex add failure Notiification
+                    return Service.START_NOT_STICKY;
+                }
             }
         }
-            
-        OwnCloudVersion ocv = new OwnCloudVersion(AccountManager.get(this).getUserData(account, AccountAuthenticator.KEY_OC_VERSION));
+
+        OwnCloudVersion ocv = new OwnCloudVersion(AccountManager.get(this).getUserData(account,
+                AccountAuthenticator.KEY_OC_VERSION));
         boolean chunked = FileUploader.chunkedUploadIsSupported(ocv);
         AbstractList<String> requestedUploads = new Vector<String>();
         String uploadKey = null;
         UploadFileOperation newUpload = null;
         try {
-            for (int i=0; i < files.length; i++) {
+            for (int i = 0; i < files.length; i++) {
                 uploadKey = buildRemoteName(account, files[i].getRemotePath());
                 if (chunked) {
-                    newUpload = new ChunkedUploadFileOperation(account, files[i], isInstant, forceOverwrite, localAction);
+                    newUpload = new ChunkedUploadFileOperation(account, files[i], isInstant, forceOverwrite,
+                            localAction);
                 } else {
                     newUpload = new UploadFileOperation(account, files[i], isInstant, forceOverwrite, localAction);
                 }
-                if (fixed && i==0) {
+                if (fixed && i == 0) {
                     newUpload.setRemoteFolderToBeCreated();
                 }
                 mPendingUploads.putIfAbsent(uploadKey, newUpload);
                 newUpload.addDatatransferProgressListener(this);
+                newUpload.addDatatransferProgressListener((FileUploaderBinder)mBinder);
                 requestedUploads.add(uploadKey);
             }
-            
+
         } catch (IllegalArgumentException e) {
-            Log.e(TAG, "Not enough information provided in intent: " + e.getMessage());
+            Log_OC.e(TAG, "Not enough information provided in intent: " + e.getMessage());
             return START_NOT_STICKY;
-            
+
         } catch (IllegalStateException e) {
-            Log.e(TAG, "Bad information provided in intent: " + e.getMessage());
+            Log_OC.e(TAG, "Bad information provided in intent: " + e.getMessage());
             return START_NOT_STICKY;
-            
+
         } catch (Exception e) {
-            Log.e(TAG, "Unexpected exception while processing upload intent", e);
+            Log_OC.e(TAG, "Unexpected exception while processing upload intent", e);
             return START_NOT_STICKY;
-            
+
         }
-        
+
         if (requestedUploads.size() > 0) {
             Message msg = mServiceHandler.obtainMessage();
             msg.arg1 = startId;
             msg.obj = requestedUploads;
             mServiceHandler.sendMessage(msg);
         }
-
+        Log_OC.i(TAG, "mPendingUploads size:" + mPendingUploads.size());
         return Service.START_NOT_STICKY;
     }
 
-    
     /**
-     * Provides a binder object that clients can use to perform operations on the queue of uploads, excepting the addition of new files. 
+     * Provides a binder object that clients can use to perform operations on
+     * the queue of uploads, excepting the addition of new files.
      * 
-     * Implemented to perform cancellation, pause and resume of existing uploads.
+     * Implemented to perform cancellation, pause and resume of existing
+     * uploads.
      */
     @Override
     public IBinder onBind(Intent arg0) {
         return mBinder;
     }
+    
+    /**
+     * Called when ALL the bound clients were onbound.
+     */
+    @Override
+    public boolean onUnbind(Intent intent) {
+        ((FileUploaderBinder)mBinder).clearListeners();
+        return false;   // not accepting rebinding (default behaviour)
+    }
+    
 
     /**
-     *  Binder to let client components to perform operations on the queue of uploads.
+     * Binder to let client components to perform operations on the queue of
+     * uploads.
      * 
-     *  It provides by itself the available operations.
+     * It provides by itself the available operations.
      */
-    public class FileUploaderBinder extends Binder {
+    public class FileUploaderBinder extends Binder implements OnDatatransferProgressListener {
+        
+        /** 
+         * Map of listeners that will be reported about progress of uploads from a {@link FileUploaderBinder} instance 
+         */
+        private Map<String, OnDatatransferProgressListener> mBoundListeners = new HashMap<String, OnDatatransferProgressListener>();
         
         /**
          * Cancels a pending or current upload of a remote file.
          * 
-         * @param account       Owncloud account where the remote file will be stored.
-         * @param file          A file in the queue of pending uploads
+         * @param account Owncloud account where the remote file will be stored.
+         * @param file A file in the queue of pending uploads
          */
         public void cancel(Account account, OCFile file) {
             UploadFileOperation upload = null;
@@ -321,20 +362,30 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
         }
         
         
+        
+        public void clearListeners() {
+            mBoundListeners.clear();
+        }
+
+
+        
+        
         /**
-         * Returns True when the file described by 'file' is being uploaded to the ownCloud account 'account' or waiting for it
+         * Returns True when the file described by 'file' is being uploaded to
+         * the ownCloud account 'account' or waiting for it
          * 
-         * If 'file' is a directory, returns 'true' if some of its descendant files is downloading or waiting to download. 
+         * If 'file' is a directory, returns 'true' if some of its descendant files is uploading or waiting to upload. 
          * 
-         * @param account       Owncloud account where the remote file will be stored.
-         * @param file          A file that could be in the queue of pending uploads
+         * @param account Owncloud account where the remote file will be stored.
+         * @param file A file that could be in the queue of pending uploads
          */
         public boolean isUploading(Account account, OCFile file) {
-            if (account == null || file == null) return false;
+            if (account == null || file == null)
+                return false;
             String targetKey = buildRemoteName(account, file);
             synchronized (mPendingUploads) {
                 if (file.isDirectory()) {
-                    // this can be slow if there are many downloads :(
+                    // this can be slow if there are many uploads :(
                     Iterator<String> it = mPendingUploads.keySet().iterator();
                     boolean found = false;
                     while (it.hasNext() && !found) {
@@ -346,19 +397,69 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
                 }
             }
         }
+
+
+        /**
+         * Adds a listener interested in the progress of the upload for a concrete file.
+         * 
+         * @param listener      Object to notify about progress of transfer.    
+         * @param account       ownCloud account holding the file of interest.
+         * @param file          {@link OCfile} of interest for listener. 
+         */
+        public void addDatatransferProgressListener (OnDatatransferProgressListener listener, Account account, OCFile file) {
+            if (account == null || file == null || listener == null) return;
+            String targetKey = buildRemoteName(account, file);
+            mBoundListeners.put(targetKey, listener);
+        }
+        
+        
+        
+        /**
+         * Removes a listener interested in the progress of the upload for a concrete file.
+         * 
+         * @param listener      Object to notify about progress of transfer.    
+         * @param account       ownCloud account holding the file of interest.
+         * @param file          {@link OCfile} of interest for listener. 
+         */
+        public void removeDatatransferProgressListener (OnDatatransferProgressListener listener, Account account, OCFile file) {
+            if (account == null || file == null || listener == null) return;
+            String targetKey = buildRemoteName(account, file);
+            if (mBoundListeners.get(targetKey) == listener) {
+                mBoundListeners.remove(targetKey);
+            }
+        }
+
+
+        @Override
+        public void onTransferProgress(long progressRate) {
+            // old way, should not be in use any more
+        }
+
+
+        @Override
+        public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer,
+                String fileName) {
+            String key = buildRemoteName(mCurrentUpload.getAccount(), mCurrentUpload.getFile());
+            OnDatatransferProgressListener boundListener = mBoundListeners.get(key);
+            if (boundListener != null) {
+                boundListener.onTransferProgress(progressRate, totalTransferredSoFar, totalToTransfer, fileName);
+            }
+        }
+        
     }
-    
-    
-    
-    
-    /** 
-     * Upload worker. Performs the pending uploads in the order they were requested. 
+
+    /**
+     * Upload worker. Performs the pending uploads in the order they were
+     * requested.
      * 
-     * Created with the Looper of a new thread, started in {@link FileUploader#onCreate()}. 
+     * Created with the Looper of a new thread, started in
+     * {@link FileUploader#onCreate()}.
      */
     private static class ServiceHandler extends Handler {
-        // don't make it a final class, and don't remove the static ; lint will warn about a possible memory leak
+        // don't make it a final class, and don't remove the static ; lint will
+        // warn about a possible memory leak
         FileUploader mService;
+
         public ServiceHandler(Looper looper, FileUploader service) {
             super(looper);
             if (service == null)
@@ -380,22 +481,20 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
         }
     }
 
-    
-    
-    
     /**
      * Core upload method: sends the file(s) to upload
      * 
-     * @param uploadKey   Key to access the upload to perform, contained in mPendingUploads
+     * @param uploadKey Key to access the upload to perform, contained in
+     *            mPendingUploads
      */
     public void uploadFile(String uploadKey) {
 
-        synchronized(mPendingUploads) {
+        synchronized (mPendingUploads) {
             mCurrentUpload = mPendingUploads.get(uploadKey);
         }
-        
+
         if (mCurrentUpload != null) {
-            
+
             notifyUploadStart(mCurrentUpload);
 
             RemoteOperationResult uploadResult = null;
@@ -410,7 +509,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
             
                 /// create remote folder for instant uploads
                 if (mCurrentUpload.isRemoteFolderToBeCreated()) {
-                    RemoteOperation operation = new CreateFolderOperation(  InstantUploadBroadcastReceiver.INSTANT_UPLOAD_DIR
+                    RemoteOperation operation = new CreateFolderOperation(  FileStorageUtils.getInstantUploadFilePath(this, "")
                                                                             mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR).getFileId(), // TODO generalize this : INSTANT_UPLOAD_DIR could not be a child of root
                                                                             mStorageManager);
                     operation.execute(mUploadClient);      // ignoring result; fail could just mean that it already exists, but local database is not synchronized; the upload will be tried anyway
@@ -424,33 +523,35 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
                 }
                 
             } catch (AccountsException e) {
-                Log.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
+                Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
                 uploadResult = new RemoteOperationResult(e);
                 
             } catch (IOException e) {
-                Log.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
+                Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
                 uploadResult = new RemoteOperationResult(e);
                 
             } finally {
-                synchronized(mPendingUploads) {
+                synchronized (mPendingUploads) {
                     mPendingUploads.remove(uploadKey);
+                    Log_OC.i(TAG, "Remove CurrentUploadItem from pending upload Item Map.");
                 }
             }
             
             /// notify result
-            notifyUploadResult(uploadResult, mCurrentUpload);
             
+            notifyUploadResult(uploadResult, mCurrentUpload);
             sendFinalBroadcast(mCurrentUpload, uploadResult);
-            
+
         }
-        
+
     }
 
     /**
      * Saves a OC File after a successful upload.
      * 
-     * A PROPFIND is necessary to keep the props in the local database synchronized with the server, 
-     * specially the modification time and Etag (where available)
+     * A PROPFIND is necessary to keep the props in the local database
+     * synchronized with the server, specially the modification time and Etag
+     * (where available)
      * 
      * TODO refactor this ugly thing
      */
@@ -458,149 +559,168 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
         OCFile file = mCurrentUpload.getFile();
         long syncDate = System.currentTimeMillis();
         file.setLastSyncDateForData(syncDate);
-        
-        /// new PROPFIND to keep data consistent with server in theory, should return the same we already have
+
+        // / new PROPFIND to keep data consistent with server in theory, should
+        // return the same we already have
         PropFindMethod propfind = null;
         RemoteOperationResult result = null;
         try {
-          propfind = new PropFindMethod(mUploadClient.getBaseUri() + WebdavUtils.encodePath(mCurrentUpload.getRemotePath()));
-          int status = mUploadClient.executeMethod(propfind);
-          boolean isMultiStatus = (status == HttpStatus.SC_MULTI_STATUS);
-          if (isMultiStatus) {
-              MultiStatus resp = propfind.getResponseBodyAsMultiStatus();
-              WebdavEntry we = new WebdavEntry(resp.getResponses()[0],
-                                               mUploadClient.getBaseUri().getPath());
-              updateOCFile(file, we);
-              file.setLastSyncDateForProperties(syncDate);
-              
-          } else {
-              mUploadClient.exhaustResponse(propfind.getResponseBodyAsStream());
-          }
-          
-          result = new RemoteOperationResult(isMultiStatus, status);
-          Log.i(TAG, "Update: synchronizing properties for uploaded " + mCurrentUpload.getRemotePath() + ": " + result.getLogMessage());
-          
+            propfind = new PropFindMethod(mUploadClient.getBaseUri()
+                    + WebdavUtils.encodePath(mCurrentUpload.getRemotePath()));
+            int status = mUploadClient.executeMethod(propfind);
+            boolean isMultiStatus = (status == HttpStatus.SC_MULTI_STATUS);
+            if (isMultiStatus) {
+                MultiStatus resp = propfind.getResponseBodyAsMultiStatus();
+                WebdavEntry we = new WebdavEntry(resp.getResponses()[0], mUploadClient.getBaseUri().getPath());
+                updateOCFile(file, we);
+                file.setLastSyncDateForProperties(syncDate);
+
+            } else {
+                mUploadClient.exhaustResponse(propfind.getResponseBodyAsStream());
+            }
+
+            result = new RemoteOperationResult(isMultiStatus, status);
+            Log_OC.i(TAG, "Update: synchronizing properties for uploaded " + mCurrentUpload.getRemotePath() + ": "
+                    + result.getLogMessage());
+
         } catch (Exception e) {
             result = new RemoteOperationResult(e);
-            Log.e(TAG, "Update: synchronizing properties for uploaded " + mCurrentUpload.getRemotePath() + ": " + result.getLogMessage(), e);
+            Log_OC.e(TAG, "Update: synchronizing properties for uploaded " + mCurrentUpload.getRemotePath() + ": "
+                    + result.getLogMessage(), e);
 
         } finally {
             if (propfind != null)
                 propfind.releaseConnection();
         }
 
-        /// maybe this would be better as part of UploadFileOperation... or maybe all this method
+        // / maybe this would be better as part of UploadFileOperation... or
+        // maybe all this method
         if (mCurrentUpload.wasRenamed()) {
             OCFile oldFile = mCurrentUpload.getOldFile();
             if (oldFile.fileExists()) {
                 oldFile.setStoragePath(null);
                 mStorageManager.saveFile(oldFile);
-                
-            } // else: it was just an automatic renaming due to a name coincidence; nothing else is needed, the storagePath is right in the instance returned by mCurrentUpload.getFile()
+
+            } // else: it was just an automatic renaming due to a name
+              // coincidence; nothing else is needed, the storagePath is right
+              // in the instance returned by mCurrentUpload.getFile()
         }
-        
+
         mStorageManager.saveFile(file);
     }
 
-    
     private void updateOCFile(OCFile file, WebdavEntry we) {
         file.setCreationTimestamp(we.createTimestamp());
         file.setFileLength(we.contentLength());
         file.setMimetype(we.contentType());
         file.setModificationTimestamp(we.modifiedTimestamp());
         file.setModificationTimestampAtLastSyncForData(we.modifiedTimestamp());
-        // file.setEtag(mCurrentDownload.getEtag());    // TODO Etag, where available
+        // file.setEtag(mCurrentUpload.getEtag());    // TODO Etag, where available
     }
-    
-    
+
     private boolean checkAndFixInstantUploadDirectory(FileDataStorageManager storageManager) {
-        OCFile instantUploadDir = storageManager.getFileByPath(InstantUploadBroadcastReceiver.INSTANT_UPLOAD_DIR);
+        String instantUploadDirPath = FileStorageUtils.getInstantUploadFilePath(this, "");
+        OCFile instantUploadDir = storageManager.getFileByPath(instantUploadDirPath);
         if (instantUploadDir == null) {
-            // first instant upload in the account, or never account not synchronized after the remote InstantUpload folder was created
-            OCFile newDir = new OCFile(InstantUploadBroadcastReceiver.INSTANT_UPLOAD_DIR);
+            // first instant upload in the account. never account not
+            // synchronized after the remote InstantUpload folder was created
+            OCFile newDir = new OCFile(instantUploadDirPath);
             newDir.setMimetype("DIR");
-            newDir.setParentId(storageManager.getFileByPath(OCFile.PATH_SEPARATOR).getFileId());
-            storageManager.saveFile(newDir);
-            return true;
+            OCFile path = storageManager.getFileByPath(OCFile.PATH_SEPARATOR);
+
+            if (path != null) {
+                newDir.setParentId(path.getFileId());
+                storageManager.saveFile(newDir);
+                return true;
+            } else {    // this should not happen anymore
+                return false;
+            }
+
         }
         return false;
     }
 
-    
-    private OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType, FileDataStorageManager storageManager) {
+    private OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType,
+            FileDataStorageManager storageManager) {
         OCFile newFile = new OCFile(remotePath);
         newFile.setStoragePath(localPath);
         newFile.setLastSyncDateForProperties(0);
         newFile.setLastSyncDateForData(0);
-        
+
         // size
         if (localPath != null && localPath.length() > 0) {
             File localFile = new File(localPath);
             newFile.setFileLength(localFile.length());
             newFile.setLastSyncDateForData(localFile.lastModified());
-        }   // don't worry about not assigning size, the problems with localPath are checked when the UploadFileOperation instance is created
-        
+        } // don't worry about not assigning size, the problems with localPath
+          // are checked when the UploadFileOperation instance is created
+
         // MIME type
         if (mimeType == null || mimeType.length() <= 0) {
             try {
-                mimeType = MimeTypeMap.getSingleton()
-                    .getMimeTypeFromExtension(
-                            remotePath.substring(remotePath.lastIndexOf('.') + 1));
+                mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
+                        remotePath.substring(remotePath.lastIndexOf('.') + 1));
             } catch (IndexOutOfBoundsException e) {
-                Log.e(TAG, "Trying to find out MIME type of a file without extension: " + remotePath);
+                Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + remotePath);
             }
         }
         if (mimeType == null) {
             mimeType = "application/octet-stream";
         }
         newFile.setMimetype(mimeType);
-        
+
         // parent dir
         String parentPath = new File(remotePath).getParent();
-        parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR ;
+        parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR;
         OCFile parentDir = storageManager.getFileByPath(parentPath);
-        if (parentDir == null) {
-            throw new IllegalStateException("Can not upload a file to a non existing remote location: " + parentPath);
-        }
         long parentDirId = parentDir.getFileId();
         newFile.setParentId(parentDirId);
         return newFile;
     }
-    
 
     /**
      * Creates a status notification to show the upload progress
      * 
-     * @param upload    Upload operation starting.
+     * @param upload Upload operation starting.
      */
+    @SuppressWarnings("deprecation")
     private void notifyUploadStart(UploadFileOperation upload) {
-        /// create status notification with a progress bar
+        // / create status notification with a progress bar
         mLastPercent = 0;
-        mNotification = new Notification(R.drawable.icon, getString(R.string.uploader_upload_in_progress_ticker), System.currentTimeMillis());
+        mNotification = new Notification(R.drawable.icon, getString(R.string.uploader_upload_in_progress_ticker),
+                System.currentTimeMillis());
         mNotification.flags |= Notification.FLAG_ONGOING_EVENT;
         mDefaultNotificationContentView = mNotification.contentView;
-        mNotification.contentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.progressbar_layout);
+        mNotification.contentView = new RemoteViews(getApplicationContext().getPackageName(),
+                R.layout.progressbar_layout);
         mNotification.contentView.setProgressBar(R.id.status_progress, 100, 0, false);
-        mNotification.contentView.setTextViewText(R.id.status_text, String.format(getString(R.string.uploader_upload_in_progress_content), 0, upload.getFileName()));
+        mNotification.contentView.setTextViewText(R.id.status_text,
+                String.format(getString(R.string.uploader_upload_in_progress_content), 0, upload.getFileName()));
         mNotification.contentView.setImageViewResource(R.id.status_icon, R.drawable.icon);
         
         /// includes a pending intent in the notification showing the details view of the file
-        Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
+        Intent showDetailsIntent = null;
+        if (PreviewImageFragment.canBePreviewed(upload.getFile())) {
+            showDetailsIntent = new Intent(this, PreviewImageActivity.class);
+        } else {
+            showDetailsIntent = new Intent(this, FileDetailActivity.class);
+            showDetailsIntent.putExtra(FileDetailActivity.EXTRA_MODE, FileDetailActivity.MODE_DETAILS);
+        }
         showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, upload.getFile());
         showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, upload.getAccount());
         showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-        mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), showDetailsIntent, 0);
-        
+        mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(),
+                (int) System.currentTimeMillis(), showDetailsIntent, 0);
+
         mNotificationManager.notify(R.string.uploader_upload_in_progress_ticker, mNotification);
     }
 
-    
     /**
      * Callback method to update the progress bar in the status notification
      */
     @Override
     public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String fileName) {
-        int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer));
+        int percent = (int) (100.0 * ((double) totalTransferredSoFar) / ((double) totalToTransfer));
         if (percent != mLastPercent) {
             mNotification.contentView.setProgressBar(R.id.status_progress, 100, percent, false);
             String text = String.format(getString(R.string.uploader_upload_in_progress_content), percent, fileName);
@@ -609,59 +729,69 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
         }
         mLastPercent = percent;
     }
-    
-    
+
     /**
-     * Callback method to update the progress bar in the status notification  (old version)
+     * Callback method to update the progress bar in the status notification
+     * (old version)
      */
     @Override
     public void onTransferProgress(long progressRate) {
         // NOTHING TO DO HERE ANYMORE
     }
 
-    
     /**
      * Updates the status notification with the result of an upload operation.
      * 
-     * @param uploadResult    Result of the upload operation.
-     * @param upload          Finished upload operation
+     * @param uploadResult Result of the upload operation.
+     * @param upload Finished upload operation
      */
     private void notifyUploadResult(RemoteOperationResult uploadResult, UploadFileOperation upload) {
+        Log_OC.d(TAG, "NotifyUploadResult with resultCode: " + uploadResult.getCode());
         if (uploadResult.isCancelled()) {
-            /// cancelled operation -> silent removal of progress notification
+            // / cancelled operation -> silent removal of progress notification
             mNotificationManager.cancel(R.string.uploader_upload_in_progress_ticker);
-            
+
         } else if (uploadResult.isSuccess()) {
-            /// success -> silent update of progress notification to success message 
-            mNotification.flags ^= Notification.FLAG_ONGOING_EVENT; // remove the ongoing flag
+            // / success -> silent update of progress notification to success
+            // message
+            mNotification.flags ^= Notification.FLAG_ONGOING_EVENT; // remove
+                                                                    // the
+                                                                    // ongoing
+                                                                    // flag
             mNotification.flags |= Notification.FLAG_AUTO_CANCEL;
             mNotification.contentView = mDefaultNotificationContentView;
             
             /// includes a pending intent in the notification showing the details view of the file
-            Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
+            Intent showDetailsIntent = null;
+            if (PreviewImageFragment.canBePreviewed(upload.getFile())) {
+                showDetailsIntent = new Intent(this, PreviewImageActivity.class); 
+            } else {
+                showDetailsIntent = new Intent(this, FileDetailActivity.class); 
+                showDetailsIntent.putExtra(FileDetailActivity.EXTRA_MODE, FileDetailActivity.MODE_DETAILS);
+            }
             showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, upload.getFile());
             showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, upload.getAccount());
             showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-            mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), showDetailsIntent, 0);
-            
-            mNotification.setLatestEventInfo(   getApplicationContext(), 
-                                                getString(R.string.uploader_upload_succeeded_ticker), 
-                                                String.format(getString(R.string.uploader_upload_succeeded_content_single), upload.getFileName()), 
-                                                mNotification.contentIntent);
-            
-            mNotificationManager.notify(R.string.uploader_upload_in_progress_ticker, mNotification);    // NOT AN ERROR; uploader_upload_in_progress_ticker is the target, not a new notification
-            
-            /* Notification about multiple uploads: pending of update
-            mNotification.setLatestEventInfo(   getApplicationContext(), 
-                                                    getString(R.string.uploader_upload_succeeded_ticker), 
-                                                    String.format(getString(R.string.uploader_upload_succeeded_content_multiple), mSuccessCounter), 
-                                                    mNotification.contentIntent);
-             */
-            
+            mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(),
+                    (int) System.currentTimeMillis(), showDetailsIntent, 0);
+
+            mNotification.setLatestEventInfo(getApplicationContext(),
+                    getString(R.string.uploader_upload_succeeded_ticker),
+                    String.format(getString(R.string.uploader_upload_succeeded_content_single), upload.getFileName()),
+                    mNotification.contentIntent);
+
+            mNotificationManager.notify(R.string.uploader_upload_in_progress_ticker, mNotification); // NOT
+                                                                                                     // AN
+            DbHandler db = new DbHandler(this.getBaseContext());
+            db.removeIUPendingFile(mCurrentUpload.getFile().getStoragePath());
+            db.close();
+
         } else {
-            /// fail -> explicit failure notification
+
+            // / fail -> explicit failure notification
             mNotificationManager.cancel(R.string.uploader_upload_in_progress_ticker);
-            Notification finalNotification = new Notification(R.drawable.icon, getString(R.string.uploader_upload_failed_ticker), System.currentTimeMillis());
+            Notification finalNotification = new Notification(R.drawable.icon,
+                    getString(R.string.uploader_upload_failed_ticker), System.currentTimeMillis());
             finalNotification.flags |= Notification.FLAG_AUTO_CANCEL;
             if (uploadResult.getCode() == ResultCode.UNAUTHORIZED) {
                 // let the user update credentials with one click
@@ -678,41 +808,75 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
                 finalNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);
             }
             
-            String content = null; 
-            if (uploadResult.getCode() == ResultCode.LOCAL_STORAGE_FULL ||
-                uploadResult.getCode() == ResultCode.LOCAL_STORAGE_NOT_COPIED) {
-                // TODO we need a class to provide error messages for the users from a RemoteOperationResult and a RemoteOperation 
-                content = String.format(getString(R.string.error__upload__local_file_not_copied), upload.getFileName(), getString(R.string.app_name));
+            String content = null;
+            if (uploadResult.getCode() == ResultCode.LOCAL_STORAGE_FULL
+                    || uploadResult.getCode() == ResultCode.LOCAL_STORAGE_NOT_COPIED) {
+                // TODO we need a class to provide error messages for the users
+                // from a RemoteOperationResult and a RemoteOperation
+                content = String.format(getString(R.string.error__upload__local_file_not_copied), upload.getFileName(),
+                        getString(R.string.app_name));
+            } else if (uploadResult.getCode() == ResultCode.QUOTA_EXCEEDED) {
+                content = getString(R.string.failed_upload_quota_exceeded_text);
             } else {
-                content = String.format(getString(R.string.uploader_upload_failed_content_single), upload.getFileName());
+                content = String
+                        .format(getString(R.string.uploader_upload_failed_content_single), upload.getFileName());
             }
-            finalNotification.setLatestEventInfo(   getApplicationContext(), 
-                                                    getString(R.string.uploader_upload_failed_ticker), 
-                                                    content, 
-                                                    finalNotification.contentIntent);
-            
+
+            // we add only for instant-uploads the InstantUploadActivity and the
+            // db entry
+            Intent detailUploadIntent = null;
+            if (upload.isInstant() && InstantUploadActivity.IS_ENABLED) {
+                detailUploadIntent = new Intent(this, InstantUploadActivity.class);
+                detailUploadIntent.putExtra(FileUploader.KEY_ACCOUNT, upload.getAccount());
+            } else {
+                detailUploadIntent = new Intent(this, FailedUploadActivity.class);
+                detailUploadIntent.putExtra(FailedUploadActivity.MESSAGE, content);
+            }
+            finalNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(),
+                    (int) System.currentTimeMillis(), detailUploadIntent, PendingIntent.FLAG_UPDATE_CURRENT
+                            | PendingIntent.FLAG_ONE_SHOT);
+
+            if (upload.isInstant()) {
+                DbHandler db = null;
+                try {
+                    db = new DbHandler(this.getBaseContext());
+                    String message = uploadResult.getLogMessage() + " errorCode: " + uploadResult.getCode();
+                    Log_OC.e(TAG, message + " Http-Code: " + uploadResult.getHttpCode());
+                    if (uploadResult.getCode() == ResultCode.QUOTA_EXCEEDED) {
+                        message = getString(R.string.failed_upload_quota_exceeded_text);
+                    }
+                    if (db.updateFileState(upload.getOriginalStoragePath(), DbHandler.UPLOAD_STATUS_UPLOAD_FAILED,
+                            message) == 0) {
+                        db.putFileForLater(upload.getOriginalStoragePath(), upload.getAccount().name, message);
+                    }
+                } finally {
+                    if (db != null) {
+                        db.close();
+                    }
+                }
+            }
+            finalNotification.setLatestEventInfo(getApplicationContext(),
+                    getString(R.string.uploader_upload_failed_ticker), content, finalNotification.contentIntent);
+
             mNotificationManager.notify(R.string.uploader_upload_failed_ticker, finalNotification);
-            
-            /* Notification about multiple uploads failure: pending of update
-            finalNotification.setLatestEventInfo(   getApplicationContext(), 
-                                                        getString(R.string.uploader_upload_failed_ticker), 
-                                                        String.format(getString(R.string.uploader_upload_failed_content_multiple), mSuccessCounter, mTotalFilesToSend), 
-                                                        finalNotification.contentIntent);
-            } */                
         }
-        
+
     }
-    
-    
+
     /**
-     * Sends a broadcast in order to the interested activities can update their view
+     * Sends a broadcast in order to the interested activities can update their
+     * view
      * 
-     * @param upload          Finished upload operation
-     * @param uploadResult    Result of the upload operation
+     * @param upload Finished upload operation
+     * @param uploadResult Result of the upload operation
      */
     private void sendFinalBroadcast(UploadFileOperation upload, RemoteOperationResult uploadResult) {
         Intent end = new Intent(UPLOAD_FINISH_MESSAGE);
-        end.putExtra(EXTRA_REMOTE_PATH, upload.getRemotePath());    // real remote path, after possible automatic renaming
+        end.putExtra(EXTRA_REMOTE_PATH, upload.getRemotePath()); // real remote
+                                                                 // path, after
+                                                                 // possible
+                                                                 // automatic
+                                                                 // renaming
         if (upload.wasRenamed()) {
             end.putExtra(EXTRA_OLD_REMOTE_PATH, upload.getOldFile().getRemotePath());
         }
@@ -722,5 +886,4 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
         sendStickyBroadcast(end);
     }
 
-
 }
index 0be14cc..b6ee1ac 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index a2b131f..a974c56 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -18,6 +17,8 @@
  */
 package com.owncloud.android.location;
 
+import com.owncloud.android.Log_OC;
+
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningServiceInfo;
 import android.content.BroadcastReceiver;
@@ -25,7 +26,6 @@ import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.preference.PreferenceManager;
-import android.util.Log;
 
 public class LocationServiceLauncherReciever extends BroadcastReceiver {
 
@@ -60,10 +60,10 @@ public class LocationServiceLauncherReciever extends BroadcastReceiver {
         deviceTrackingIntent
                 .setAction("com.owncloud.android.location.LocationUpdateService");
         if (!isDeviceTrackingServiceRunning(context) && trackDevice) {
-            Log.d(TAG, "Starting device tracker service");
+            Log_OC.d(TAG, "Starting device tracker service");
             context.startService(deviceTrackingIntent);
         } else if (isDeviceTrackingServiceRunning(context) && !trackDevice) {
-            Log.d(TAG, "Stopping device tracker service");
+            Log_OC.d(TAG, "Stopping device tracker service");
             context.stopService(deviceTrackingIntent);
         }
     }
index 1b6b28a..7fe2ee9 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -28,9 +27,9 @@ import android.location.LocationManager;
 import android.location.LocationProvider;
 import android.os.Bundle;
 import android.preference.PreferenceManager;
-import android.util.Log;
 import android.widget.Toast;
 
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.R;
 
 public class LocationUpdateService extends IntentService implements
@@ -76,7 +75,7 @@ public class LocationUpdateService extends IntentService implements
 
         // If we do shall track the device -> Stop
         if (!trackDevice) {
-            Log.d(TAG, "Devicetracking is disabled");
+            Log_OC.d(TAG, "Devicetracking is disabled");
             stopSelf();
             return;
         }
@@ -87,7 +86,7 @@ public class LocationUpdateService extends IntentService implements
 
     @Override
     public void onLocationChanged(Location location) {
-        Log.d(TAG, "Location changed: " + location);
+        Log_OC.d(TAG, "Location changed: " + location);
 
     }
 
diff --git a/src/com/owncloud/android/media/MediaControlView.java b/src/com/owncloud/android/media/MediaControlView.java
new file mode 100644 (file)
index 0000000..8047f02
--- /dev/null
@@ -0,0 +1,472 @@
+/* ownCloud Android client application
+ * 
+ *   Copyright (C) 2012-2013  ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License 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.media;
+
+import android.content.Context;
+import android.media.MediaPlayer;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+import android.widget.MediaController.MediaPlayerControl;
+import android.widget.ProgressBar;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.TextView;
+
+import java.util.Formatter;
+import java.util.Locale;
+
+import com.owncloud.android.R;
+
+/**
+ * View containing controls for a {@link MediaPlayer}. 
+ * 
+ * Holds buttons "play / pause", "rewind", "fast forward" 
+ * and a progress slider. 
+ * 
+ * It synchronizes itself with the state of the 
+ * {@link MediaPlayer}.
+ * 
+ * @author David A. Velasco
+ */
+
+public class MediaControlView extends FrameLayout /* implements OnLayoutChangeListener, OnTouchListener */ implements OnClickListener, OnSeekBarChangeListener {
+
+    private MediaPlayerControl  mPlayer;
+    private Context             mContext;
+    private View                mRoot;
+    private ProgressBar         mProgress;
+    private TextView            mEndTime, mCurrentTime;
+    private boolean             mDragging;
+    private static final int    SHOW_PROGRESS = 1;
+    StringBuilder               mFormatBuilder;
+    Formatter                   mFormatter;
+    private ImageButton         mPauseButton;
+    private ImageButton         mFfwdButton;
+    private ImageButton         mRewButton;
+    
+    public MediaControlView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mContext = context;
+        
+        FrameLayout.LayoutParams frameParams = new FrameLayout.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT
+        );
+        LayoutInflater inflate = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        mRoot = inflate.inflate(R.layout.media_control, null);
+        initControllerView(mRoot);
+        addView(mRoot, frameParams);
+        
+        setFocusable(true);
+        setFocusableInTouchMode(true);
+        setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
+        requestFocus();
+    }
+
+    @Override
+    public void onFinishInflate() {
+        /*
+        if (mRoot != null)
+            initControllerView(mRoot);
+         */
+    }
+
+    /* TODO REMOVE
+    public MediaControlView(Context context, boolean useFastForward) {
+        super(context);
+        mContext = context;
+        mUseFastForward = useFastForward;
+        initFloatingWindowLayout();
+        //initFloatingWindow();
+    }
+    */
+
+    /* TODO REMOVE
+    public MediaControlView(Context context) {
+        this(context, true);
+    }
+    */
+    
+    /* T
+    private void initFloatingWindow() {
+        mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
+        mWindow = PolicyManager.makeNewWindow(mContext);
+        mWindow.setWindowManager(mWindowManager, null, null);
+        mWindow.requestFeature(Window.FEATURE_NO_TITLE);
+        mDecor = mWindow.getDecorView();
+        mDecor.setOnTouchListener(mTouchListener);
+        mWindow.setContentView(this);
+        mWindow.setBackgroundDrawableResource(android.R.color.transparent);
+        
+        // While the media controller is up, the volume control keys should
+        // affect the media stream type
+        mWindow.setVolumeControlStream(AudioManager.STREAM_MUSIC);
+
+        setFocusable(true);
+        setFocusableInTouchMode(true);
+        setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
+        requestFocus();
+    }
+    */
+
+    /*
+    // Allocate and initialize the static parts of mDecorLayoutParams. Must
+    // also call updateFloatingWindowLayout() to fill in the dynamic parts
+    // (y and width) before mDecorLayoutParams can be used.
+    private void initFloatingWindowLayout() {
+        mDecorLayoutParams = new WindowManager.LayoutParams();
+        WindowManager.LayoutParams p = mDecorLayoutParams;
+        p.gravity = Gravity.TOP;
+        p.height = LayoutParams.WRAP_CONTENT;
+        p.x = 0;
+        p.format = PixelFormat.TRANSLUCENT;
+        p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+        p.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
+                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
+        p.token = null;
+        p.windowAnimations = 0; // android.R.style.DropDownAnimationDown;
+    }
+    */
+
+    // Update the dynamic parts of mDecorLayoutParams
+    // Must be called with mAnchor != NULL.
+    /*
+    private void updateFloatingWindowLayout() {
+        int [] anchorPos = new int[2];
+        mAnchor.getLocationOnScreen(anchorPos);
+
+        WindowManager.LayoutParams p = mDecorLayoutParams;
+        p.width = mAnchor.getWidth();
+        p.y = anchorPos[1] + mAnchor.getHeight();
+    }
+    */
+
+    /*
+    // This is called whenever mAnchor's layout bound changes
+    public void onLayoutChange(View v, int left, int top, int right,
+            int bottom, int oldLeft, int oldTop, int oldRight,
+            int oldBottom) {
+        //updateFloatingWindowLayout();
+        if (mShowing) {
+            mWindowManager.updateViewLayout(mDecor, mDecorLayoutParams);
+        }
+    }
+    */
+    
+    /*
+    public boolean onTouch(View v, MotionEvent event) {
+        if (event.getAction() == MotionEvent.ACTION_DOWN) {
+            if (mShowing) {
+                hide();
+            }
+        }
+            return false;
+    }
+    */
+    
+    
+    public void setMediaPlayer(MediaPlayerControl player) {
+        mPlayer = player;
+        mHandler.sendEmptyMessage(SHOW_PROGRESS);
+        updatePausePlay();
+    }
+
+    
+    private void initControllerView(View v) {
+        mPauseButton = (ImageButton) v.findViewById(R.id.playBtn);
+        if (mPauseButton != null) {
+            mPauseButton.requestFocus();
+            mPauseButton.setOnClickListener(this);
+        }
+
+        mFfwdButton = (ImageButton) v.findViewById(R.id.forwardBtn);
+        if (mFfwdButton != null) {
+            mFfwdButton.setOnClickListener(this);
+        }
+
+        mRewButton = (ImageButton) v.findViewById(R.id.rewindBtn);
+        if (mRewButton != null) {
+            mRewButton.setOnClickListener(this);
+        }
+
+        mProgress = (ProgressBar) v.findViewById(R.id.progressBar);
+        if (mProgress != null) {
+            if (mProgress instanceof SeekBar) {
+                SeekBar seeker = (SeekBar) mProgress;
+                seeker.setOnSeekBarChangeListener(this);
+            }
+            mProgress.setMax(1000);
+        }
+
+        mEndTime = (TextView) v.findViewById(R.id.totalTimeText);
+        mCurrentTime = (TextView) v.findViewById(R.id.currentTimeText);
+        mFormatBuilder = new StringBuilder();
+        mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
+
+    }
+
+    
+    /**
+     * Disable pause or seek buttons if the stream cannot be paused or seeked.
+     * This requires the control interface to be a MediaPlayerControlExt
+     */
+    private void disableUnsupportedButtons() {
+        try {
+            if (mPauseButton != null && !mPlayer.canPause()) {
+                mPauseButton.setEnabled(false);
+            }
+            if (mRewButton != null && !mPlayer.canSeekBackward()) {
+                mRewButton.setEnabled(false);
+            }
+            if (mFfwdButton != null && !mPlayer.canSeekForward()) {
+                mFfwdButton.setEnabled(false);
+            }
+        } catch (IncompatibleClassChangeError ex) {
+            // We were given an old version of the interface, that doesn't have
+            // the canPause/canSeekXYZ methods. This is OK, it just means we
+            // assume the media can be paused and seeked, and so we don't disable
+            // the buttons.
+        }
+    }
+    
+    
+    private Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            int pos;
+            switch (msg.what) {
+                case SHOW_PROGRESS:
+                    pos = setProgress();
+                    if (!mDragging) {
+                        msg = obtainMessage(SHOW_PROGRESS);
+                        sendMessageDelayed(msg, 1000 - (pos % 1000));
+                    }
+                    break;
+            }
+        }
+    };
+
+    private String stringForTime(int timeMs) {
+        int totalSeconds = timeMs / 1000;
+
+        int seconds = totalSeconds % 60;
+        int minutes = (totalSeconds / 60) % 60;
+        int hours   = totalSeconds / 3600;
+
+        mFormatBuilder.setLength(0);
+        if (hours > 0) {
+            return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString();
+        } else {
+            return mFormatter.format("%02d:%02d", minutes, seconds).toString();
+        }
+    }
+
+    private int setProgress() {
+        if (mPlayer == null || mDragging) {
+            return 0;
+        }
+        int position = mPlayer.getCurrentPosition();
+        int duration = mPlayer.getDuration();
+        if (mProgress != null) {
+            if (duration > 0) {
+                // use long to avoid overflow
+                long pos = 1000L * position / duration;
+                mProgress.setProgress( (int) pos);
+            }
+            int percent = mPlayer.getBufferPercentage();
+            mProgress.setSecondaryProgress(percent * 10);
+        }
+
+        if (mEndTime != null)
+            mEndTime.setText(stringForTime(duration));
+        if (mCurrentTime != null)
+            mCurrentTime.setText(stringForTime(position));
+
+        return position;
+    }
+    
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        int keyCode = event.getKeyCode();
+        final boolean uniqueDown = event.getRepeatCount() == 0
+                && event.getAction() == KeyEvent.ACTION_DOWN;
+        if (keyCode ==  KeyEvent.KEYCODE_HEADSETHOOK
+                || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
+                || keyCode == KeyEvent.KEYCODE_SPACE) {
+            if (uniqueDown) {
+                doPauseResume();
+                //show(sDefaultTimeout);
+                if (mPauseButton != null) {
+                    mPauseButton.requestFocus();
+                }
+            }
+            return true;
+        } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
+            if (uniqueDown && !mPlayer.isPlaying()) {
+                mPlayer.start();
+                updatePausePlay();
+                //show(sDefaultTimeout);
+            }
+            return true;
+        } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
+                || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
+            if (uniqueDown && mPlayer.isPlaying()) {
+                mPlayer.pause();
+                updatePausePlay();
+                //show(sDefaultTimeout);
+            }
+            return true;
+        }
+
+        //show(sDefaultTimeout);
+        return super.dispatchKeyEvent(event);
+    }
+
+    public void updatePausePlay() {
+        if (mRoot == null || mPauseButton == null)
+            return;
+
+        if (mPlayer.isPlaying()) {
+            mPauseButton.setImageResource(android.R.drawable.ic_media_pause);
+        } else {
+            mPauseButton.setImageResource(android.R.drawable.ic_media_play);
+        }
+    }
+
+    private void doPauseResume() {
+        if (mPlayer.isPlaying()) {
+            mPlayer.pause();
+        } else {
+            mPlayer.start();
+        }
+        updatePausePlay();
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        if (mPauseButton != null) {
+            mPauseButton.setEnabled(enabled);
+        }
+        if (mFfwdButton != null) {
+            mFfwdButton.setEnabled(enabled);
+        }
+        if (mRewButton != null) {
+            mRewButton.setEnabled(enabled);
+        }
+        if (mProgress != null) {
+            mProgress.setEnabled(enabled);
+        }
+        disableUnsupportedButtons();
+        super.setEnabled(enabled);
+    }
+
+    @Override
+    public void onClick(View v) {
+        int pos;
+        boolean playing = mPlayer.isPlaying();
+        switch (v.getId()) {
+        
+        case R.id.playBtn: 
+            doPauseResume();
+            break;
+
+        case R.id.rewindBtn:
+            pos = mPlayer.getCurrentPosition();
+            pos -= 5000;
+            mPlayer.seekTo(pos);
+            if (!playing) mPlayer.pause();  // necessary in some 2.3.x devices 
+            setProgress();
+            break;
+
+        case R.id.forwardBtn:
+            pos = mPlayer.getCurrentPosition();
+            pos += 15000;
+            mPlayer.seekTo(pos);
+            if (!playing) mPlayer.pause(); // necessary in some 2.3.x devices
+            setProgress();
+            break;
+        
+        }
+    }
+    
+    
+    @Override
+    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+        if (!fromUser) {
+            // We're not interested in programmatically generated changes to
+            // the progress bar's position.
+            return;
+        }
+
+        long duration = mPlayer.getDuration();
+        long newposition = (duration * progress) / 1000L;
+        mPlayer.seekTo( (int) newposition);
+        if (mCurrentTime != null)
+            mCurrentTime.setText(stringForTime( (int) newposition));
+    }
+    
+    /**
+     * Called in devices with touchpad when the user starts to adjust the 
+     * position of the seekbar's thumb.
+     * 
+     * Will be followed by several onProgressChanged notifications.
+     */
+    @Override
+    public void onStartTrackingTouch(SeekBar seekBar) {
+        mDragging = true;                           // monitors the duration of dragging 
+        mHandler.removeMessages(SHOW_PROGRESS);     // grants no more updates with media player progress while dragging 
+    }
+
+    
+    /**
+     * Called in devices with touchpad when the user finishes the
+     * adjusting of the seekbar.
+     */
+    @Override
+    public void onStopTrackingTouch(SeekBar seekBar) {
+        mDragging = false;
+        setProgress();
+        updatePausePlay();
+        mHandler.sendEmptyMessage(SHOW_PROGRESS);    // grants future updates with media player progress 
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+        event.setClassName(MediaControlView.class.getName());
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        info.setClassName(MediaControlView.class.getName());
+    }
+    
+}
diff --git a/src/com/owncloud/android/media/MediaService.java b/src/com/owncloud/android/media/MediaService.java
new file mode 100644 (file)
index 0000000..9bb0625
--- /dev/null
@@ -0,0 +1,705 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License 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.media;
+
+import android.accounts.Account;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnCompletionListener;
+import android.media.MediaPlayer.OnErrorListener;
+import android.media.MediaPlayer.OnPreparedListener;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiManager.WifiLock;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.widget.Toast;
+
+import java.io.IOException;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.ui.activity.FileDetailActivity;
+import com.owncloud.android.ui.fragment.FileDetailFragment;
+
+/**
+ * Service that handles media playback, both audio and video. 
+ * 
+ * Waits for Intents which signal the service to perform specific operations: Play, Pause,
+ * Rewind, etc.
+ * 
+ * @author David A. Velasco
+ */
+public class MediaService extends Service implements OnCompletionListener, OnPreparedListener,
+                OnErrorListener, AudioManager.OnAudioFocusChangeListener {
+
+    private static final String TAG = MediaService.class.getSimpleName();
+
+    private static final String MY_PACKAGE = MediaService.class.getPackage() != null ? MediaService.class.getPackage().getName() : "com.owncloud.android.media";
+    
+    /// Intent actions that we are prepared to handle
+    public static final String ACTION_PLAY_FILE = MY_PACKAGE + ".action.PLAY_FILE";
+    public static final String ACTION_STOP_ALL = MY_PACKAGE + ".action.STOP_ALL";
+
+    /// Keys to add extras to the action
+    public static final String EXTRA_FILE = MY_PACKAGE + ".extra.FILE";
+    public static final String EXTRA_ACCOUNT = MY_PACKAGE + ".extra.ACCOUNT";
+    public static String EXTRA_START_POSITION = MY_PACKAGE + ".extra.START_POSITION";
+    public static final String EXTRA_PLAY_ON_LOAD = MY_PACKAGE + ".extra.PLAY_ON_LOAD";
+
+
+    /** Error code for specific messages - see regular error codes at {@link MediaPlayer} */
+    public static final int OC_MEDIA_ERROR = 0;
+
+    /** Time To keep the control panel visible when the user does not use it */
+    public static final int MEDIA_CONTROL_SHORT_LIFE = 4000;
+    
+    /** Time To keep the control panel visible when the user does not use it */
+    public static final int MEDIA_CONTROL_PERMANENT = 0;
+    
+    /** Volume to set when audio focus is lost and ducking is allowed */
+    private static final float DUCK_VOLUME = 0.1f;
+
+    /** Media player instance */
+    private MediaPlayer mPlayer = null;
+    
+    /** Reference to the system AudioManager */
+    private AudioManager mAudioManager = null;
+
+    
+    /** Values to indicate the state of the service */
+    enum State {
+        STOPPED,
+        PREPARING,      
+        PLAYING,        
+        PAUSED 
+    };
+    
+
+    /** Current state */
+    private State mState = State.STOPPED;
+    
+    /** Possible focus values */
+    enum AudioFocus {
+        NO_FOCUS,            
+        NO_FOCUS_CAN_DUCK,   
+        FOCUS           
+    }
+    
+    /** Current focus state */
+    private AudioFocus mAudioFocus = AudioFocus.NO_FOCUS;
+    
+
+    /** 'True' when the current song is streaming from the network */
+    private boolean mIsStreaming = false;
+
+    /** Wifi lock kept to prevents the device from shutting off the radio when streaming a file. */
+    private WifiLock mWifiLock;
+    
+    private static final String MEDIA_WIFI_LOCK_TAG = MY_PACKAGE + ".WIFI_LOCK";
+
+    /** Notification to keep in the notification bar while a song is playing */
+    private NotificationManager mNotificationManager;
+    private Notification mNotification = null;
+
+    /** File being played */
+    private OCFile mFile;
+    
+    /** Account holding the file being played */
+    private Account mAccount;
+
+    /** Flag signaling if the audio should be played immediately when the file is prepared */ 
+    protected boolean mPlayOnPrepared;
+
+    /** Position, in miliseconds, where the audio should be started */
+    private int mStartPosition;
+    
+    /** Interface to access the service through binding */
+    private IBinder mBinder;
+
+    /** Control panel shown to the user to control the playback, to register through binding */
+    private MediaControlView mMediaController;
+    
+
+    
+    /**
+     * Helper method to get an error message suitable to show to users for errors occurred in media playback,
+     * 
+     * @param context   A context to access string resources.
+     * @param what      See {@link MediaPlayer.OnErrorListener#onError(MediaPlayer, int, int)
+     * @param extra     See {@link MediaPlayer.OnErrorListener#onError(MediaPlayer, int, int)
+     * @return          Message suitable to users.
+     */
+    public static String getMessageForMediaError(Context context, int what, int extra) {
+        int messageId;
+        
+        if (what == OC_MEDIA_ERROR) {
+            messageId = extra;
+                
+        } else if (extra == MediaPlayer.MEDIA_ERROR_UNSUPPORTED) {
+            /*  Added in API level 17
+                Bitstream is conforming to the related coding standard or file spec, but the media framework does not support the feature.
+                Constant Value: -1010 (0xfffffc0e)
+             */
+            messageId = R.string.media_err_unsupported;
+
+        } else if (extra == MediaPlayer.MEDIA_ERROR_IO) {
+            /*  Added in API level 17
+                File or network related operation errors.
+                Constant Value: -1004 (0xfffffc14) 
+             */
+            messageId = R.string.media_err_io;
+            
+        } else if (extra == MediaPlayer.MEDIA_ERROR_MALFORMED) {
+            /*  Added in API level 17
+                Bitstream is not conforming to the related coding standard or file spec.
+                Constant Value: -1007 (0xfffffc11) 
+             */
+            messageId = R.string.media_err_malformed;
+            
+        } else if (extra == MediaPlayer.MEDIA_ERROR_TIMED_OUT) {
+            /*  Added in API level 17
+                Some operation takes too long to complete, usually more than 3-5 seconds.
+                Constant Value: -110 (0xffffff92)
+            */
+            messageId = R.string.media_err_timeout;
+
+        } else if (what == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
+            /*  Added in API level 3
+                The video is streamed and its container is not valid for progressive playback i.e the video's index (e.g moov atom) is not at the start of the file.
+                Constant Value: 200 (0x000000c8)
+            */
+            messageId = R.string.media_err_invalid_progressive_playback;
+                
+        } else {
+            /*  MediaPlayer.MEDIA_ERROR_UNKNOWN
+                Added in API level 1
+                Unspecified media player error.
+                Constant Value: 1 (0x00000001)
+            */
+            /*  MediaPlayer.MEDIA_ERROR_SERVER_DIED)
+                Added in API level 1
+                Media server died. In this case, the application must release the MediaPlayer object and instantiate a new one.
+                Constant Value: 100 (0x00000064) 
+             */
+            messageId = R.string.media_err_unknown;
+        }
+        return context.getString(messageId);
+    }
+
+
+    
+    /**
+     * Initialize a service instance
+     * 
+     * {@inheritDoc}
+     */
+    @Override
+    public void onCreate() {
+        Log_OC.d(TAG, "Creating ownCloud media service");
+
+        mWifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE)).
+                createWifiLock(WifiManager.WIFI_MODE_FULL, MEDIA_WIFI_LOCK_TAG);
+
+        mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+        mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
+        mBinder = new MediaServiceBinder(this);
+    }
+
+    
+    /**
+     * Entry point for Intents requesting actions, sent here via startService.
+     * 
+     * {@inheritDoc}
+     */
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        String action = intent.getAction();
+        if (action.equals(ACTION_PLAY_FILE)) { 
+            processPlayFileRequest(intent);
+            
+        } else if (action.equals(ACTION_STOP_ALL)) {
+            processStopRequest(true);
+        }
+
+        return START_NOT_STICKY; // don't want it to restart in case it's killed.
+    }
+
+
+    /**
+     * Processes a request to play a media file received as a parameter
+     * 
+     * TODO If a new request is received when a file is being prepared, it is ignored. Is this what we want? 
+     * 
+     * @param intent    Intent received in the request with the data to identify the file to play. 
+     */
+    private void processPlayFileRequest(Intent intent) {
+        if (mState != State.PREPARING) {
+            mFile = intent.getExtras().getParcelable(EXTRA_FILE);
+            mAccount = intent.getExtras().getParcelable(EXTRA_ACCOUNT);
+            mPlayOnPrepared = intent.getExtras().getBoolean(EXTRA_PLAY_ON_LOAD, false);
+            mStartPosition = intent.getExtras().getInt(EXTRA_START_POSITION, 0);
+            tryToGetAudioFocus();
+            playMedia();
+        }
+    }
+
+    
+    /**
+     * Processes a request to play a media file.
+     */
+    protected void processPlayRequest() {
+        // request audio focus
+        tryToGetAudioFocus();
+
+        // actually play the song
+        if (mState == State.STOPPED) {
+            // (re)start playback
+            playMedia();
+            
+        } else if (mState == State.PAUSED) {
+            // continue playback
+            mState = State.PLAYING;
+            setUpAsForeground(String.format(getString(R.string.media_state_playing), mFile.getFileName()));
+            configAndStartMediaPlayer();
+            
+        }
+    }
+
+    
+    /**
+     * Makes sure the media player exists and has been reset. This will create the media player
+     * if needed. reset the existing media player if one already exists.
+     */
+    protected void createMediaPlayerIfNeeded() {
+        if (mPlayer == null) {
+            mPlayer = new MediaPlayer();
+
+            // make sure the CPU won't go to sleep while media is playing
+            mPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
+
+            // the media player will notify the service when it's ready preparing, and when it's done playing
+            mPlayer.setOnPreparedListener(this);
+            mPlayer.setOnCompletionListener(this);
+            mPlayer.setOnErrorListener(this);
+            
+        } else {
+            mPlayer.reset();
+        }
+    }
+
+    /**
+     * Processes a request to pause the current playback 
+     */
+    protected void processPauseRequest() {
+        if (mState == State.PLAYING) {
+            mState = State.PAUSED;
+            mPlayer.pause();
+            releaseResources(false); // retain media player in pause
+            // TODO polite audio focus, instead of keep it owned; or not?
+        }
+    }
+    
+    
+    /**
+     * Processes a request to stop the playback.
+     * 
+     * @param   force       When 'true', the playback is stopped no matter the value of mState
+     */
+    protected void processStopRequest(boolean force) {
+        if (mState != State.PREPARING || force) {
+            mState = State.STOPPED;
+            mFile = null;
+            mAccount = null;
+            releaseResources(true);
+            giveUpAudioFocus();
+            stopSelf();     // service is no longer necessary
+        }
+    }
+    
+
+    /**
+     * Releases resources used by the service for playback. This includes the "foreground service"
+     * status and notification, the wake locks and possibly the MediaPlayer.
+     *
+     * @param releaseMediaPlayer    Indicates whether the Media Player should also be released or not
+     */
+    protected void releaseResources(boolean releaseMediaPlayer) {
+        // stop being a foreground service
+        stopForeground(true);
+
+        // stop and release the Media Player, if it's available
+        if (releaseMediaPlayer && mPlayer != null) {
+            mPlayer.reset();
+            mPlayer.release();
+            mPlayer = null;
+        }
+
+        // release the Wifi lock, if holding it
+        if (mWifiLock.isHeld()) {
+            mWifiLock.release();
+        }
+    }
+
+    
+    /**
+     * Fully releases the audio focus.
+     */
+    private void giveUpAudioFocus() {
+        if (mAudioFocus == AudioFocus.FOCUS 
+                && mAudioManager != null  
+                && AudioManager.AUDIOFOCUS_REQUEST_GRANTED == mAudioManager.abandonAudioFocus(this))  {
+            
+            mAudioFocus = AudioFocus.NO_FOCUS;
+        }
+    }
+
+    
+    /**
+     * Reconfigures MediaPlayer according to audio focus settings and starts/restarts it. 
+     */
+    protected void configAndStartMediaPlayer() {
+        if (mPlayer == null) {
+            throw new IllegalStateException("mPlayer is NULL");
+        }
+        
+        if (mAudioFocus == AudioFocus.NO_FOCUS) {
+            if (mPlayer.isPlaying()) {
+                mPlayer.pause();        // have to be polite; but mState is not changed, to resume when focus is received again
+            }
+            
+        }  else { 
+            if (mAudioFocus == AudioFocus.NO_FOCUS_CAN_DUCK) {
+                mPlayer.setVolume(DUCK_VOLUME, DUCK_VOLUME);
+                
+            } else {
+                mPlayer.setVolume(1.0f, 1.0f); // full volume
+            }
+    
+            if (!mPlayer.isPlaying()) {
+                mPlayer.start();
+            }
+        }
+    }
+
+    
+    /**
+     * Requests the audio focus to the Audio Manager 
+     */
+    private void tryToGetAudioFocus() {
+        if (mAudioFocus != AudioFocus.FOCUS 
+                && mAudioManager != null 
+                && (AudioManager.AUDIOFOCUS_REQUEST_GRANTED == mAudioManager.requestAudioFocus( this,
+                                                                                                AudioManager.STREAM_MUSIC, 
+                                                                                                AudioManager.AUDIOFOCUS_GAIN))
+                ) {
+            mAudioFocus = AudioFocus.FOCUS;
+        }
+    }
+
+    
+    /**
+     * Starts playing the current media file. 
+     */
+    protected void playMedia() {
+        mState = State.STOPPED;
+        releaseResources(false); // release everything except MediaPlayer
+
+        try {
+            if (mFile == null) { 
+                Toast.makeText(this, R.string.media_err_nothing_to_play, Toast.LENGTH_LONG).show();
+                processStopRequest(true);
+                return;
+                
+            } else if (mAccount == null) {
+                Toast.makeText(this, R.string.media_err_not_in_owncloud, Toast.LENGTH_LONG).show();
+                processStopRequest(true);
+                return;
+            }
+
+            createMediaPlayerIfNeeded();
+            mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+            String url = mFile.getStoragePath();
+            /* Streaming is not possible right now
+            if (url == null || url.length() <= 0) {
+                url = AccountUtils.constructFullURLForAccount(this, mAccount) + mFile.getRemotePath();
+            }
+            mIsStreaming = url.startsWith("http:") || url.startsWith("https:");
+            */
+            mIsStreaming = false;
+            
+            mPlayer.setDataSource(url);
+
+            mState = State.PREPARING;
+            setUpAsForeground(String.format(getString(R.string.media_state_loading), mFile.getFileName()));
+
+            // starts preparing the media player in background
+            mPlayer.prepareAsync();
+
+            // prevent the Wifi from going to sleep when streaming
+            if (mIsStreaming) { 
+                mWifiLock.acquire();
+            } else if (mWifiLock.isHeld()) {
+                mWifiLock.release();
+            }
+            
+        } catch (SecurityException e) {
+            Log_OC.e(TAG, "SecurityException playing " + mAccount.name + mFile.getRemotePath(), e);
+            Toast.makeText(this, String.format(getString(R.string.media_err_security_ex), mFile.getFileName()), Toast.LENGTH_LONG).show();
+            processStopRequest(true);
+            
+        } catch (IOException e) {
+            Log_OC.e(TAG, "IOException playing " + mAccount.name + mFile.getRemotePath(), e);
+            Toast.makeText(this, String.format(getString(R.string.media_err_io_ex), mFile.getFileName()), Toast.LENGTH_LONG).show();
+            processStopRequest(true);
+            
+        } catch (IllegalStateException e) {
+            Log_OC.e(TAG, "IllegalStateException " + mAccount.name + mFile.getRemotePath(), e);
+            Toast.makeText(this, String.format(getString(R.string.media_err_unexpected), mFile.getFileName()), Toast.LENGTH_LONG).show();
+            processStopRequest(true);
+            
+        } catch (IllegalArgumentException e) {
+            Log_OC.e(TAG, "IllegalArgumentException " + mAccount.name + mFile.getRemotePath(), e);
+            Toast.makeText(this, String.format(getString(R.string.media_err_unexpected), mFile.getFileName()), Toast.LENGTH_LONG).show();
+            processStopRequest(true);
+        }
+    }
+
+    
+    /** Called when media player is done playing current song. */
+    public void onCompletion(MediaPlayer player) {
+        Toast.makeText(this, String.format(getString(R.string.media_event_done, mFile.getFileName())), Toast.LENGTH_LONG).show();
+        if (mMediaController != null) {
+            // somebody is still bound to the service
+            player.seekTo(0);
+            processPauseRequest();
+            mMediaController.updatePausePlay();
+        } else {
+            // nobody is bound
+            processStopRequest(true);
+        }
+        return;
+    }
+    
+
+    /** 
+     * Called when media player is done preparing. 
+     *
+     * Time to start.
+     */
+    public void onPrepared(MediaPlayer player) {
+        mState = State.PLAYING;
+        updateNotification(String.format(getString(R.string.media_state_playing), mFile.getFileName()));
+        if (mMediaController != null) {
+            mMediaController.setEnabled(true);
+        }
+        player.seekTo(mStartPosition);
+        configAndStartMediaPlayer();
+        if (!mPlayOnPrepared) {
+            processPauseRequest();
+        }
+        
+        if (mMediaController != null) {
+            mMediaController.updatePausePlay();
+        }
+    }
+    
+
+    /** 
+     * Updates the status notification
+     */
+    @SuppressWarnings("deprecation")
+    private void updateNotification(String content) {
+        // TODO check if updating the Intent is really necessary
+        Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
+        showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, mFile);
+        showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, mAccount);
+        showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), 
+                                                                (int)System.currentTimeMillis(), 
+                                                                showDetailsIntent, 
+                                                                PendingIntent.FLAG_UPDATE_CURRENT);
+        mNotification.when = System.currentTimeMillis();
+        //mNotification.contentView.setTextViewText(R.id.status_text, content);
+        String ticker = String.format(getString(R.string.media_notif_ticker), getString(R.string.app_name));
+        mNotification.setLatestEventInfo(getApplicationContext(), ticker, content, mNotification.contentIntent);
+        mNotificationManager.notify(R.string.media_notif_ticker, mNotification);
+    }
+
+    
+    /**
+     * Configures the service as a foreground service.
+     * 
+     * The system will avoid finishing the service as much as possible when resources as low.
+     * 
+     * A notification must be created to keep the user aware of the existance of the service.
+     */
+    @SuppressWarnings("deprecation")
+    private void setUpAsForeground(String content) {
+        /// creates status notification
+        // TODO put a progress bar to follow the playback progress
+        mNotification = new Notification();
+        mNotification.icon = android.R.drawable.ic_media_play;
+        //mNotification.tickerText = text;
+        mNotification.when = System.currentTimeMillis();
+        mNotification.flags |= Notification.FLAG_ONGOING_EVENT;
+        //mNotification.contentView.setTextViewText(R.id.status_text, "ownCloud Music Player");     // NULL POINTER
+        //mNotification.contentView.setTextViewText(R.id.status_text, getString(R.string.downloader_download_in_progress_content));
+
+        
+        /// includes a pending intent in the notification showing the details view of the file
+        Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
+        showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, mFile);
+        showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, mAccount);
+        showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), 
+                                                                (int)System.currentTimeMillis(), 
+                                                                showDetailsIntent, 
+                                                                PendingIntent.FLAG_UPDATE_CURRENT);
+        
+        
+        //mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotification);
+        String ticker = String.format(getString(R.string.media_notif_ticker), getString(R.string.app_name));
+        mNotification.setLatestEventInfo(getApplicationContext(), ticker, content, mNotification.contentIntent);
+        startForeground(R.string.media_notif_ticker, mNotification);
+        
+    }
+
+    /**
+     * Called when there's an error playing media. 
+     * 
+     * Warns the user about the error and resets the media player.
+     */
+    public boolean onError(MediaPlayer mp, int what, int extra) {
+        Log_OC.e(TAG, "Error in audio playback, what = " + what + ", extra = " + extra);
+        
+        String message = getMessageForMediaError(this, what, extra);
+        Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
+        
+        processStopRequest(true);
+        return true; 
+    }
+
+    /**
+     * Called by the system when another app tries to play some sound.
+     * 
+     * {@inheritDoc}
+     */
+    @Override
+    public void onAudioFocusChange(int focusChange) {
+        if (focusChange > 0) {
+            // focus gain; check AudioManager.AUDIOFOCUS_* values
+            mAudioFocus = AudioFocus.FOCUS;
+            // restart media player with new focus settings
+            if (mState == State.PLAYING)
+                configAndStartMediaPlayer();
+            
+        } else if (focusChange < 0) {
+            // focus loss; check AudioManager.AUDIOFOCUS_* values
+            boolean canDuck = AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK == focusChange;
+                mAudioFocus = canDuck ? AudioFocus.NO_FOCUS_CAN_DUCK : AudioFocus.NO_FOCUS;
+                // start/restart/pause media player with new focus settings
+                if (mPlayer != null && mPlayer.isPlaying())
+                    configAndStartMediaPlayer();
+        }
+        
+    }
+
+    /**
+     * Called when the service is finished for final clean-up.
+     * 
+     * {@inheritDoc}
+     */
+    @Override
+    public void onDestroy() {
+        mState = State.STOPPED;
+        releaseResources(true);
+        giveUpAudioFocus();
+    }
+    
+
+    /**
+     * Provides a binder object that clients can use to perform operations on the MediaPlayer managed by the MediaService. 
+     */
+    @Override
+    public IBinder onBind(Intent arg) {
+        return mBinder;
+    }
+    
+    
+    /**
+     * Called when ALL the bound clients were onbound.
+     * 
+     * The service is destroyed if playback stopped or paused
+     */
+    @Override
+    public boolean onUnbind(Intent intent) {
+        if (mState == State.PAUSED || mState == State.STOPPED)  {
+            processStopRequest(false);
+        }
+        return false;   // not accepting rebinding (default behaviour)
+    }
+
+
+    /**
+     * Accesses the current MediaPlayer instance in the service.
+     * 
+     * To be handled carefully. Visibility is protected to be accessed only 
+     * 
+     * @return Current MediaPlayer instance handled by MediaService.
+     */
+    protected MediaPlayer getPlayer() {
+        return mPlayer;
+    }
+
+
+    /**
+     * Accesses the current OCFile loaded in the service.
+     * 
+     * @return  The current OCFile loaded in the service.
+     */
+    protected OCFile getCurrentFile() {
+        return mFile;
+    }
+
+    
+    /**
+     * Accesses the current {@link State} of the MediaService.
+     * 
+     * @return  The current {@link State} of the MediaService.
+     */
+    protected State getState() {
+        return mState;
+    }
+
+
+    protected void setMediaContoller(MediaControlView mediaController) {
+        mMediaController = mediaController;
+    }
+
+    protected MediaControlView getMediaController() {
+        return mMediaController;
+    }
+
+}
diff --git a/src/com/owncloud/android/media/MediaServiceBinder.java b/src/com/owncloud/android/media/MediaServiceBinder.java
new file mode 100644 (file)
index 0000000..4fab8bd
--- /dev/null
@@ -0,0 +1,181 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License 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.media;
+
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.media.MediaService.State;
+
+import android.accounts.Account;
+import android.content.Intent;
+import android.media.MediaPlayer;
+import android.os.Binder;
+import android.widget.MediaController;
+
+
+/**
+ *  Binder allowing client components to perform operations on on the MediaPlayer managed by a MediaService instance.
+ * 
+ *  Provides the operations of {@link MediaController.MediaPlayerControl}, and an extra method to check if
+ *  an {@link OCFile} instance is handled by the MediaService.
+ *  
+ *  @author David A. Velasco
+ */
+public class MediaServiceBinder extends Binder implements MediaController.MediaPlayerControl {
+
+    private static final String TAG = MediaServiceBinder.class.getSimpleName();
+    /**
+     * {@link MediaService} instance to access with the binder
+     */
+    private MediaService mService = null;
+    
+    /**
+     * Public constructor
+     * 
+     * @param service       A {@link MediaService} instance to access with the binder 
+     */
+    public MediaServiceBinder(MediaService service) {
+        if (service == null) {
+            throw new IllegalArgumentException("Argument 'service' can not be null");
+        }
+        mService = service;
+    }
+    
+    
+    public boolean isPlaying(OCFile mFile) {
+        return (mFile != null && mFile.equals(mService.getCurrentFile())); 
+    }
+
+    
+    @Override
+    public boolean canPause() {
+        return true;
+    }
+
+    @Override
+    public boolean canSeekBackward() {
+        return true;
+    }
+
+    @Override
+    public boolean canSeekForward() {
+        return true;
+    }
+
+    @Override
+    public int getBufferPercentage() {
+        MediaPlayer currentPlayer = mService.getPlayer();
+        if (currentPlayer != null) {
+            return 100;
+            // TODO update for streamed playback; add OnBufferUpdateListener in MediaService
+        } else {
+            return 0;
+        }
+    }
+
+    @Override
+    public int getCurrentPosition() {
+        MediaPlayer currentPlayer = mService.getPlayer();
+        if (currentPlayer != null) {
+            int pos = currentPlayer.getCurrentPosition();
+            return pos;
+        } else {
+            return 0;
+        }
+    }
+
+    @Override
+    public int getDuration() {
+        MediaPlayer currentPlayer = mService.getPlayer();
+        if (currentPlayer != null) {
+            int dur = currentPlayer.getDuration();
+            return dur;
+        } else {
+            return 0;
+        }
+    }
+
+    
+    /**
+     * Reports if the MediaService is playing a file or not.
+     * 
+     * Considers that the file is being played when it is in preparation because the expected
+     * client of this method is a {@link MediaController} , and we do not want that the 'play'
+     * button is shown when the file is being prepared by the MediaService.
+     */
+    @Override
+    public boolean isPlaying() {
+        MediaService.State currentState = mService.getState();
+        return (currentState == State.PLAYING || (currentState == State.PREPARING && mService.mPlayOnPrepared));
+    }
+
+    
+    @Override
+    public void pause() {
+        Log_OC.d(TAG, "Pausing through binder...");
+        mService.processPauseRequest();
+    }
+
+    @Override
+    public void seekTo(int pos) {
+        Log_OC.d(TAG, "Seeking " + pos + " through binder...");
+        MediaPlayer currentPlayer = mService.getPlayer();
+        MediaService.State currentState = mService.getState();
+        if (currentPlayer != null && currentState != State.PREPARING && currentState != State.STOPPED) {
+            currentPlayer.seekTo(pos);
+        }
+    }
+
+    @Override
+    public void start() {
+        Log_OC.d(TAG, "Starting through binder...");
+        mService.processPlayRequest();  // this will finish the service if there is no file preloaded to play
+    }
+    
+    public void start(Account account, OCFile file, boolean playImmediately, int position) {
+        Log_OC.d(TAG, "Loading and starting through binder...");
+        Intent i = new Intent(mService, MediaService.class);
+        i.putExtra(MediaService.EXTRA_ACCOUNT, account);
+        i.putExtra(MediaService.EXTRA_FILE, file);
+        i.putExtra(MediaService.EXTRA_PLAY_ON_LOAD, playImmediately);
+        i.putExtra(MediaService.EXTRA_START_POSITION, position);
+        i.setAction(MediaService.ACTION_PLAY_FILE);
+        mService.startService(i);
+    }
+
+
+    public void registerMediaController(MediaControlView mediaController) {
+        mService.setMediaContoller(mediaController);
+    }
+    
+    public void unregisterMediaController(MediaControlView mediaController) {
+        if (mediaController != null && mediaController == mService.getMediaController()) {
+            mService.setMediaContoller(null);
+        }
+        
+    }
+
+    public boolean isInPlaybackState() {
+        MediaService.State currentState = mService.getState();
+        return (currentState == MediaService.State.PLAYING || currentState == MediaService.State.PAUSED);
+    }
+
+}
+
+
index c206ca7..da9a0ff 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -40,7 +39,7 @@ import org.apache.commons.httpclient.params.HttpConnectionParams;
 import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
 import org.apache.http.conn.ssl.X509HostnameVerifier;
 
-import android.util.Log;
+import com.owncloud.android.Log_OC;
 
 /**
  * AdvancedSSLProtocolSocketFactory allows to create SSL {@link Socket}s with 
@@ -104,13 +103,13 @@ public class AdvancedSslSocketFactory implements ProtocolSocketFactory {
             final InetAddress localAddress, final int localPort,
             final HttpConnectionParams params) throws IOException,
             UnknownHostException, ConnectTimeoutException {
-        Log.d(TAG, "Creating SSL Socket with remote " + host + ":" + port + ", local " + localAddress + ":" + localPort + ", params: " + params);
+        Log_OC.d(TAG, "Creating SSL Socket with remote " + host + ":" + port + ", local " + localAddress + ":" + localPort + ", params: " + params);
         if (params == null) {
             throw new IllegalArgumentException("Parameters may not be null");
         } 
         int timeout = params.getConnectionTimeout();
         SocketFactory socketfactory = mSslContext.getSocketFactory();
-        Log.d(TAG, " ... with connection timeout " + timeout + " and socket timeout " + params.getSoTimeout());
+        Log_OC.d(TAG, " ... with connection timeout " + timeout + " and socket timeout " + params.getSoTimeout());
         Socket socket = socketfactory.createSocket();
         SocketAddress localaddr = new InetSocketAddress(localAddress, localPort);
         SocketAddress remoteaddr = new InetSocketAddress(host, port);
@@ -126,7 +125,7 @@ public class AdvancedSslSocketFactory implements ProtocolSocketFactory {
      */
     public Socket createSocket(String host, int port) throws IOException,
             UnknownHostException {
-        Log.d(TAG, "Creating SSL Socket with remote " + host + ":" + port);
+        Log_OC.d(TAG, "Creating SSL Socket with remote " + host + ":" + port);
         Socket socket = mSslContext.getSocketFactory().createSocket(host, port);
         verifyPeerIdentity(host, port, socket);
         return socket; 
index d3c7924..ad4feb3 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -32,7 +31,7 @@ import javax.net.ssl.TrustManager;
 import javax.net.ssl.TrustManagerFactory;
 import javax.net.ssl.X509TrustManager;
 
-import android.util.Log;
+import com.owncloud.android.Log_OC;
 
 /**
  * @author David A. Velasco
@@ -139,7 +138,7 @@ public class AdvancedX509TrustManager implements X509TrustManager {
         try {
             return (mKnownServersKeyStore.getCertificateAlias(cert) != null);
         } catch (KeyStoreException e) {
-            Log.d(TAG, "Fail while checking certificate in the known-servers store");
+            Log_OC.d(TAG, "Fail while checking certificate in the known-servers store");
             return false;
         }
     }
index 7739822..a426734 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012  ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 3 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -28,7 +27,7 @@ import org.apache.commons.httpclient.auth.AuthenticationException;
 import org.apache.commons.httpclient.auth.InvalidCredentialsException;
 import org.apache.commons.httpclient.auth.MalformedChallengeException;
 
-import android.util.Log;
+import com.owncloud.android.Log_OC;
 
 /**
  * Bearer authentication scheme as defined in RFC 6750.
@@ -120,7 +119,7 @@ public class BearerAuthScheme implements AuthScheme /*extends RFC2617Scheme*/ {
      * @deprecated Use {@link #authenticate(Credentials, HttpMethod)}
      */
     public String authenticate(Credentials credentials, String method, String uri) throws AuthenticationException {
-        Log.d(TAG, "enter BearerScheme.authenticate(Credentials, String, String)");
+        Log_OC.d(TAG, "enter BearerScheme.authenticate(Credentials, String, String)");
 
         BearerCredentials bearer = null;
         try {
@@ -155,7 +154,7 @@ public class BearerAuthScheme implements AuthScheme /*extends RFC2617Scheme*/ {
      * @return a basic authorization string
      */
     public String authenticate(Credentials credentials, HttpMethod method) throws AuthenticationException {
-        Log.d(TAG, "enter BearerScheme.authenticate(Credentials, HttpMethod)");
+        Log_OC.d(TAG, "enter BearerScheme.authenticate(Credentials, HttpMethod)");
 
         if (method == null) {
             throw new IllegalArgumentException("Method may not be null");
@@ -199,7 +198,7 @@ public class BearerAuthScheme implements AuthScheme /*extends RFC2617Scheme*/ {
      * @since 3.0
      */
     public static String authenticate(BearerCredentials credentials, String charset) {
-        Log.d(TAG, "enter BearerAuthScheme.authenticate(BearerCredentials, String)");
+        Log_OC.d(TAG, "enter BearerAuthScheme.authenticate(BearerCredentials, String)");
 
         if (credentials == null) {
             throw new IllegalArgumentException("Credentials may not be null"); 
index 59596e8..50799b0 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012  ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 3 of the License, or
- *   (at your option) any later version.
+ *   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
index b924ba3..e96d9dc 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index 7b2b64f..5d852d8 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -39,6 +38,7 @@ import org.apache.http.conn.ssl.X509HostnameVerifier;
 
 import com.owncloud.android.AccountUtils;
 import com.owncloud.android.authentication.AccountAuthenticator;
+import com.owncloud.android.Log_OC;
 
 import eu.alefzero.webdav.WebdavClient;
 
@@ -51,7 +51,6 @@ import android.app.Activity;
 import android.content.Context;
 import android.net.Uri;
 import android.os.Bundle;
-import android.util.Log;
 
 public class OwnCloudClientUtils {
     
@@ -86,7 +85,7 @@ public class OwnCloudClientUtils {
      * @throws IOException                  If there was some I/O error while getting the authorization token for the account.
      */
     public static WebdavClient createOwnCloudClient (Account account, Context appContext) throws OperationCanceledException, AuthenticatorException, IOException {
-        //Log.d(TAG, "Creating WebdavClient associated to " + account.name);
+        //Log_OC.d(TAG, "Creating WebdavClient associated to " + account.name);
        
         Uri uri = Uri.parse(AccountUtils.constructFullURLForAccount(appContext, account));
         WebdavClient client = createOwnCloudClient(uri, appContext);
@@ -130,7 +129,6 @@ public class OwnCloudClientUtils {
         
         return client;
     }
-
     
     /**
      * Creates a WebdavClient to access a URL and sets the desired parameters for ownCloud client connections.
@@ -140,16 +138,16 @@ public class OwnCloudClientUtils {
      * @return          A WebdavClient object ready to be used
      */
     public static WebdavClient createOwnCloudClient(Uri uri, Context context) {
-        //Log.d(TAG, "Creating WebdavClient for " + uri);
+        //Log_OC.d(TAG, "Creating WebdavClient for " + uri);
         
         //allowSelfsignedCertificates(true);
         try {
             registerAdvancedSslContext(true, context);
         }  catch (GeneralSecurityException e) {
-            Log.e(TAG, "Advanced SSL Context could not be loaded. Default SSL management in the system will be used for HTTPS connections", e);
+            Log_OC.e(TAG, "Advanced SSL Context could not be loaded. Default SSL management in the system will be used for HTTPS connections", e);
             
         } catch (IOException e) {
-            Log.e(TAG, "The local server truststore could not be read. Default SSL management in the system will be used for HTTPS connections", e);
+            Log_OC.e(TAG, "The local server truststore could not be read. Default SSL management in the system will be used for HTTPS connections", e);
         }
         
         WebdavClient client = new WebdavClient(getMultiThreadedConnManager());
@@ -227,7 +225,7 @@ public class OwnCloudClientUtils {
             //mKnownServersStore = KeyStore.getInstance("BKS");
             mKnownServersStore = KeyStore.getInstance(KeyStore.getDefaultType());
             File localTrustStoreFile = new File(context.getFilesDir(), LOCAL_TRUSTSTORE_FILENAME);
-            Log.d(TAG, "Searching known-servers store at " + localTrustStoreFile.getAbsolutePath());
+            Log_OC.d(TAG, "Searching known-servers store at " + localTrustStoreFile.getAbsolutePath());
             if (localTrustStoreFile.exists()) {
                 InputStream in = new FileInputStream(localTrustStoreFile);
                 try {
diff --git a/src/com/owncloud/android/network/ProgressiveDataTransferer.java b/src/com/owncloud/android/network/ProgressiveDataTransferer.java
new file mode 100644 (file)
index 0000000..c6fa545
--- /dev/null
@@ -0,0 +1,32 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License 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.network;
+
+import java.util.Collection;
+
+import eu.alefzero.webdav.OnDatatransferProgressListener;
+
+public interface ProgressiveDataTransferer {
+
+    public void addDatatransferProgressListener (OnDatatransferProgressListener listener);
+    
+    public void addDatatransferProgressListeners(Collection<OnDatatransferProgressListener> listeners);
+
+    public void removeDatatransferProgressListener(OnDatatransferProgressListener listener);
+
+}
index c01ee46..697c154 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -28,10 +27,11 @@ import java.util.Random;
 import org.apache.commons.httpclient.HttpException;
 import org.apache.commons.httpclient.methods.PutMethod;
 
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.network.ProgressiveDataTransferer;
 
 import android.accounts.Account;
-import android.util.Log;
 
 import eu.alefzero.webdav.ChunkFromFileChannelRequestEntity;
 import eu.alefzero.webdav.WebdavClient;
@@ -62,19 +62,19 @@ public class ChunkedUploadFileOperation extends UploadFileOperation {
             File file = new File(getStoragePath());
             raf = new RandomAccessFile(file, "r");
             channel = raf.getChannel();
-            ChunkFromFileChannelRequestEntity entity = new ChunkFromFileChannelRequestEntity(channel, getMimeType(), CHUNK_SIZE, file);
-            entity.addOnDatatransferProgressListeners(getDataTransferListeners());
+            mEntity = new ChunkFromFileChannelRequestEntity(channel, getMimeType(), CHUNK_SIZE, file);
+            ((ProgressiveDataTransferer)mEntity).addDatatransferProgressListeners(getDataTransferListeners());
             long offset = 0;
             String uriPrefix = client.getBaseUri() + WebdavUtils.encodePath(getRemotePath()) + "-chunking-" + Math.abs((new Random()).nextInt(9000)+1000) + "-" ;
             long chunkCount = (long) Math.ceil((double)file.length() / CHUNK_SIZE);
             for (int chunkIndex = 0; chunkIndex < chunkCount ; chunkIndex++, offset += CHUNK_SIZE) {
                 mPutMethod = new PutMethod(uriPrefix + chunkCount + "-" + chunkIndex);
                 mPutMethod.addRequestHeader(OC_CHUNKED_HEADER, OC_CHUNKED_HEADER);
-                entity.setOffset(offset);
-                mPutMethod.setRequestEntity(entity);
+                ((ChunkFromFileChannelRequestEntity)mEntity).setOffset(offset);
+                mPutMethod.setRequestEntity(mEntity);
                 status = client.executeMethod(mPutMethod);
                 client.exhaustResponse(mPutMethod.getResponseBodyAsStream());
-                Log.d(TAG, "Upload of " + getStoragePath() + " to " + getRemotePath() + ", chunk index " + chunkIndex + ", count " + chunkCount + ", HTTP result status " + status);
+                Log_OC.d(TAG, "Upload of " + getStoragePath() + " to " + getRemotePath() + ", chunk index " + chunkIndex + ", count " + chunkCount + ", HTTP result status " + status);
                 if (!isSuccess(status))
                     break;
             }
index 4d42478..5965db3 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 3 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -20,11 +19,10 @@ package com.owncloud.android.operations;
 
 import org.apache.jackrabbit.webdav.client.methods.MkColMethod;
 
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.datamodel.DataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 
-import android.util.Log;
-
 import eu.alefzero.webdav.WebdavClient;
 import eu.alefzero.webdav.WebdavUtils;
 
@@ -79,12 +77,12 @@ public class CreateFolderOperation extends RemoteOperation {
             }
 
             result = new RemoteOperationResult(mkcol.succeeded(), status);
-            Log.d(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage());
+            Log_OC.d(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage());
             client.exhaustResponse(mkcol.getResponseBodyAsStream());
                 
         } catch (Exception e) {
             result = new RemoteOperationResult(e);
-            Log.e(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage(), e);
+            Log_OC.e(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage(), e);
             
         } finally {
             if (mkcol != null)
index 75bf923..5745b97 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -33,6 +32,7 @@ import org.apache.commons.httpclient.HttpException;
 import org.apache.commons.httpclient.methods.GetMethod;
 import org.apache.http.HttpStatus;
 
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.operations.RemoteOperation;
 import com.owncloud.android.operations.RemoteOperationResult;
@@ -42,7 +42,6 @@ import eu.alefzero.webdav.OnDatatransferProgressListener;
 import eu.alefzero.webdav.WebdavClient;
 import eu.alefzero.webdav.WebdavUtils;
 import android.accounts.Account;
-import android.util.Log;
 import android.webkit.MimeTypeMap;
 
 /**
@@ -104,7 +103,7 @@ public class DownloadFileOperation extends RemoteOperation {
                     .getMimeTypeFromExtension(
                             mFile.getRemotePath().substring(mFile.getRemotePath().lastIndexOf('.') + 1));
             } catch (IndexOutOfBoundsException e) {
-                Log.e(TAG, "Trying to find out MIME type of a file without extension: " + mFile.getRemotePath());
+                Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + mFile.getRemotePath());
             }
         }
         if (mimeType == null) {
@@ -123,9 +122,17 @@ public class DownloadFileOperation extends RemoteOperation {
     
     
     public void addDatatransferProgressListener (OnDatatransferProgressListener listener) {
-        mDataTransferListeners.add(listener);
+        synchronized (mDataTransferListeners) {
+            mDataTransferListeners.add(listener);
+        }
     }
     
+    public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) {
+        synchronized (mDataTransferListeners) {
+            mDataTransferListeners.remove(listener);
+        }
+    }
+
     @Override
     protected RemoteOperationResult run(WebdavClient client) {
         RemoteOperationResult result = null;
@@ -148,11 +155,11 @@ public class DownloadFileOperation extends RemoteOperation {
                 result = new RemoteOperationResult(RemoteOperationResult.ResultCode.LOCAL_STORAGE_NOT_MOVED);
             else
                 result = new RemoteOperationResult(isSuccess(status), status);
-            Log.i(TAG, "Download of " + mFile.getRemotePath() + " to " + getSavePath() + ": " + result.getLogMessage());
+            Log_OC.i(TAG, "Download of " + mFile.getRemotePath() + " to " + getSavePath() + ": " + result.getLogMessage());
             
         } catch (Exception e) {
             result = new RemoteOperationResult(e);
-            Log.e(TAG, "Download of " + mFile.getRemotePath() + " to " + getSavePath() + ": " + result.getLogMessage(), e);
+            Log_OC.e(TAG, "Download of " + mFile.getRemotePath() + " to " + getSavePath() + ": " + result.getLogMessage(), e);
         }
         
         return result;
@@ -190,9 +197,11 @@ public class DownloadFileOperation extends RemoteOperation {
                     }
                     fos.write(bytes, 0, readResult);
                     transferred += readResult;
-                    it = mDataTransferListeners.iterator();
-                    while (it.hasNext()) {
-                        it.next().onTransferProgress(readResult, transferred, mFile.getFileLength(), targetFile.getName());
+                    synchronized (mDataTransferListeners) {
+                        it = mDataTransferListeners.iterator();
+                        while (it.hasNext()) {
+                            it.next().onTransferProgress(readResult, transferred, mFile.getFileLength(), targetFile.getName());
+                        }
                     }
                 }
                 savedFile = true;
@@ -221,4 +230,5 @@ public class DownloadFileOperation extends RemoteOperation {
         mCancellationRequested.set(true);   // atomic set; there is no need of synchronizing it
     }
 
+
 }
index d678ac3..1093986 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 3 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -21,10 +20,11 @@ package com.owncloud.android.operations;
 import org.apache.commons.httpclient.HttpStatus;
 import org.apache.commons.httpclient.methods.HeadMethod;
 
+import com.owncloud.android.Log_OC;
+
 import eu.alefzero.webdav.WebdavClient;
 import android.content.Context;
 import android.net.ConnectivityManager;
-import android.util.Log;
 
 /**
  * Operation to check the existence or absence of a path in a remote server.
@@ -70,11 +70,11 @@ public class ExistenceCheckOperation extends RemoteOperation {
             client.exhaustResponse(head.getResponseBodyAsStream());
             boolean success = (status == HttpStatus.SC_OK && !mSuccessIfAbsent) || (status == HttpStatus.SC_NOT_FOUND && mSuccessIfAbsent);
             result = new RemoteOperationResult(success, status);
-            Log.d(TAG, "Existence check for " + client.getBaseUri() + mPath + " targeting for " + (mSuccessIfAbsent ? " absence " : " existence ") + "finished with HTTP status " + status + (!success?"(FAIL)":""));
+            Log_OC.d(TAG, "Existence check for " + client.getBaseUri() + mPath + " targeting for " + (mSuccessIfAbsent ? " absence " : " existence ") + "finished with HTTP status " + status + (!success?"(FAIL)":""));
             
         } catch (Exception e) {
             result = new RemoteOperationResult(e);
-            Log.e(TAG, "Existence check for " + client.getBaseUri() + mPath + " targeting for " + (mSuccessIfAbsent ? " absence " : " existence ") + ": " + result.getLogMessage(), result.getException());
+            Log_OC.e(TAG, "Existence check for " + client.getBaseUri() + mPath + " targeting for " + (mSuccessIfAbsent ? " absence " : " existence ") + ": " + result.getLogMessage(), result.getException());
             
         } finally {
             if (head != null)
index b64ebce..6d43caf 100644 (file)
@@ -8,11 +8,10 @@ import org.apache.commons.httpclient.NameValuePair;
 import org.json.JSONException;
 import org.json.JSONObject;
 
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.authentication.OAuth2Constants;
 import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
 
-import android.util.Log;
-
 import eu.alefzero.webdav.WebdavClient;
 
 public class OAuth2GetAccessToken extends RemoteOperation {
@@ -98,16 +97,16 @@ public class OAuth2GetAccessToken extends RemoteOperation {
                 postMethod.releaseConnection();    // let the connection available for other methods
             
             if (result.isSuccess()) {
-                Log.i(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + result.getLogMessage());
+                Log_OC.i(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + result.getLogMessage());
             
             } else if (result.getException() != null) {
-                Log.e(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + result.getLogMessage(), result.getException());
+                Log_OC.e(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + result.getLogMessage(), result.getException());
                 
             } else if (result.getCode() == ResultCode.OAUTH2_ERROR) {
-                    Log.e(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + ((mResultTokenMap != null) ? mResultTokenMap.get(OAuth2Constants.KEY_ERROR) : "NULL"));
+                Log_OC.e(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + ((mResultTokenMap != null) ? mResultTokenMap.get(OAuth2Constants.KEY_ERROR) : "NULL"));
                     
             } else {
-                Log.e(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + result.getLogMessage());
+                Log_OC.e(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + result.getLogMessage());
             }
         }
         
@@ -135,7 +134,7 @@ public class OAuth2GetAccessToken extends RemoteOperation {
                     sb.append(value + "\n");
                 }
 
-                Log.v(TAG, "[" + i + "," + j + "] = " + p);
+                Log_OC.v(TAG, "[" + i + "," + j + "] = " + p);
                 j++;
             }
             i++;
index f2119a8..e6a58e7 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index 975d63f..0b7878c 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index 7132c53..1afcf6e 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -24,13 +23,13 @@ import org.json.JSONException;
 import org.json.JSONObject;
 
 import com.owncloud.android.AccountUtils;
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.utils.OwnCloudVersion;
 
 import eu.alefzero.webdav.WebdavClient;
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.Uri;
-import android.util.Log;
 
 public class OwnCloudServerCheckOperation extends RemoteOperation {
     
@@ -96,13 +95,13 @@ public class OwnCloudServerCheckOperation extends RemoteOperation {
         }
         
         if (mLatestResult.isSuccess()) {
-            Log.i(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage());
+            Log_OC.i(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage());
             
         } else if (mLatestResult.getException() != null) {
-            Log.e(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage(), mLatestResult.getException());
+            Log_OC.e(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage(), mLatestResult.getException());
             
         } else {
-            Log.e(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage());
+            Log_OC.e(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage());
         }
 
         return retval;
@@ -127,7 +126,7 @@ public class OwnCloudServerCheckOperation extends RemoteOperation {
             client.setBaseUri(Uri.parse("https://" + mUrl + AccountUtils.STATUS_PATH));
             boolean httpsSuccess = tryConnection(client, "https://" + mUrl + AccountUtils.STATUS_PATH); 
             if (!httpsSuccess && !mLatestResult.isSslRecoverableException()) {
-                Log.d(TAG, "establishing secure connection failed, trying non secure connection");
+                Log_OC.d(TAG, "establishing secure connection failed, trying non secure connection");
                 client.setBaseUri(Uri.parse("http://" + mUrl + AccountUtils.STATUS_PATH));
                 tryConnection(client, "http://" + mUrl + AccountUtils.STATUS_PATH);
             }
index e7d4034..711a72b 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -21,6 +20,7 @@ import java.io.IOException;
 
 import org.apache.commons.httpclient.Credentials;
 
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.authentication.AccountAuthenticator;
 import com.owncloud.android.network.BearerCredentials;
 import com.owncloud.android.network.OwnCloudClientUtils;
@@ -32,7 +32,6 @@ import android.accounts.AccountsException;
 import android.app.Activity;
 import android.content.Context;
 import android.os.Handler;
-import android.util.Log;
 
 import eu.alefzero.webdav.WebdavClient;
 
@@ -93,7 +92,7 @@ public abstract class RemoteOperation implements Runnable {
         try {
             mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext);
         } catch (Exception e) {
-            Log.e(TAG, "Error while trying to access to " + mAccount.name, e);
+            Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, e);
             return new RemoteOperationResult(e);
         }
         return run(mClient);
@@ -231,11 +230,11 @@ public abstract class RemoteOperation implements Runnable {
                 }
             
             } catch (IOException e) {
-                Log.e(TAG, "Error while trying to access to " + mAccount.name, new AccountsException("I/O exception while trying to authorize the account", e));
+                Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, new AccountsException("I/O exception while trying to authorize the account", e));
                 result = new RemoteOperationResult(e);
             
             } catch (AccountsException e) {
-                Log.e(TAG, "Error while trying to access to " + mAccount.name, e);
+                Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, e);
                 result = new RemoteOperationResult(e);
             }
        
index 14cf78f..16b4779 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -33,21 +32,24 @@ import org.apache.commons.httpclient.HttpException;
 import org.apache.commons.httpclient.HttpStatus;
 import org.apache.jackrabbit.webdav.DavException;
 
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.network.CertificateCombinedException;
 
-
 /**
  * The result of a remote operation required to an ownCloud server.
  * 
- * Provides a common classification of remote operation results for all the application. 
+ * Provides a common classification of remote operation results for all the
+ * application.
  * 
  * @author David A. Velasco
  */
 public class RemoteOperationResult implements Serializable {
-    
+
     /** Generated - should be refreshed every time the class changes!! */
     private static final long serialVersionUID = -7805531062432602444L;
     
+    private static final String TAG = "RemoteOperationResult";
+    
     public enum ResultCode { 
         OK,
         OK_SSL,
@@ -74,102 +76,108 @@ public class RemoteOperationResult implements Serializable {
         LOCAL_STORAGE_FULL, 
         LOCAL_STORAGE_NOT_MOVED, 
         LOCAL_STORAGE_NOT_COPIED, 
-        OAUTH2_ERROR_ACCESS_DENIED
+        OAUTH2_ERROR_ACCESS_DENIED,
+        QUOTA_EXCEEDED
     }
 
     private boolean mSuccess = false;
     private int mHttpCode = -1;
     private Exception mException = null;
     private ResultCode mCode = ResultCode.UNKNOWN_ERROR;
-    
+
     public RemoteOperationResult(ResultCode code) {
         mCode = code;
         mSuccess = (code == ResultCode.OK || code == ResultCode.OK_SSL || code == ResultCode.OK_NO_SSL);
     }
-    
+
     public RemoteOperationResult(boolean success, int httpCode) {
-        mSuccess = success; 
+        mSuccess = success;
         mHttpCode = httpCode;
 
         if (success) {
             mCode = ResultCode.OK;
-            
+
         } else if (httpCode > 0) {
             switch (httpCode) {
-                case HttpStatus.SC_UNAUTHORIZED:
-                    mCode = ResultCode.UNAUTHORIZED;
-                    break;
-                case HttpStatus.SC_NOT_FOUND:
-                    mCode = ResultCode.FILE_NOT_FOUND;
-                    break;
-                case HttpStatus.SC_INTERNAL_SERVER_ERROR:
-                    mCode = ResultCode.INSTANCE_NOT_CONFIGURED;
-                    break;
-                case HttpStatus.SC_CONFLICT:
-                    mCode = ResultCode.CONFLICT;
-                    break;
-                default:
-                    mCode = ResultCode.UNHANDLED_HTTP_CODE;
+            case HttpStatus.SC_UNAUTHORIZED:
+                mCode = ResultCode.UNAUTHORIZED;
+                break;
+            case HttpStatus.SC_NOT_FOUND:
+                mCode = ResultCode.FILE_NOT_FOUND;
+                break;
+            case HttpStatus.SC_INTERNAL_SERVER_ERROR:
+                mCode = ResultCode.INSTANCE_NOT_CONFIGURED;
+                break;
+            case HttpStatus.SC_CONFLICT:
+                mCode = ResultCode.CONFLICT;
+                break;
+            case HttpStatus.SC_INSUFFICIENT_STORAGE:
+                mCode = ResultCode.QUOTA_EXCEEDED;
+                break;
+            default:
+                mCode = ResultCode.UNHANDLED_HTTP_CODE;
+                Log_OC.d(TAG, "RemoteOperationResult has prcessed UNHANDLED_HTTP_CODE: " + httpCode);
             }
         }
     }
-    
+
     public RemoteOperationResult(Exception e) {
-        mException = e; 
-        
+        mException = e;
+
         if (e instanceof OperationCancelledException) {
             mCode = ResultCode.CANCELLED;
-            
-        } else if (e instanceof SocketException) {  
+
+        } else if (e instanceof SocketException) {
             mCode = ResultCode.WRONG_CONNECTION;
-        
+
         } else if (e instanceof SocketTimeoutException) {
             mCode = ResultCode.TIMEOUT;
-        
+
         } else if (e instanceof ConnectTimeoutException) {
             mCode = ResultCode.TIMEOUT;
-            
+
         } else if (e instanceof MalformedURLException) {
             mCode = ResultCode.INCORRECT_ADDRESS;
-        
+
         } else if (e instanceof UnknownHostException) {
             mCode = ResultCode.HOST_NOT_AVAILABLE;
-        
+
         } else if (e instanceof SSLException || e instanceof RuntimeException) {
             CertificateCombinedException se = getCertificateCombinedException(e);
             if (se != null) {
                 mException = se;
-                if (se.isRecoverable()) { 
+                if (se.isRecoverable()) {
                     mCode = ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED;
                 }
-                
-            } else { 
+            } else if (e instanceof RuntimeException) {
+                mCode = ResultCode.HOST_NOT_AVAILABLE;
+
+            } else {
                 mCode = ResultCode.SSL_ERROR;
             }
-            
+
         } else {
             mCode = ResultCode.UNKNOWN_ERROR;
         }
-        
+
     }
-    
-    
+
     public boolean isSuccess() {
         return mSuccess;
     }
-    
+
     public boolean isCancelled() {
         return mCode == ResultCode.CANCELLED;
     }
-    
+
     public int getHttpCode() {
         return mHttpCode;
     }
-    
+
     public ResultCode getCode() {
         return mCode;
     }
-    
+
     public Exception getException() {
         return mException;
     }
@@ -177,11 +185,11 @@ public class RemoteOperationResult implements Serializable {
     public boolean isSslRecoverableException() {
         return mCode == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED;
     }
-    
+
     private CertificateCombinedException getCertificateCombinedException(Exception e) {
         CertificateCombinedException result = null;
         if (e instanceof CertificateCombinedException) {
-            return (CertificateCombinedException)e;
+            return (CertificateCombinedException) e;
         }
         Throwable cause = mException.getCause();
         Throwable previousCause = null;
@@ -190,39 +198,38 @@ public class RemoteOperationResult implements Serializable {
             cause = cause.getCause();
         }
         if (cause != null && cause instanceof CertificateCombinedException) {
-            result = (CertificateCombinedException)cause; 
+            result = (CertificateCombinedException) cause;
         }
         return result;
     }
-    
-    
+
     public String getLogMessage() {
-        
+
         if (mException != null) {
             if (mException instanceof OperationCancelledException) {
                 return "Operation cancelled by the caller";
-                
-            } else if (mException instanceof SocketException) {  
+
+            } else if (mException instanceof SocketException) {
                 return "Socket exception";
-        
+
             } else if (mException instanceof SocketTimeoutException) {
                 return "Socket timeout exception";
-        
+
             } else if (mException instanceof ConnectTimeoutException) {
                 return "Connect timeout exception";
-            
+
             } else if (mException instanceof MalformedURLException) {
                 return "Malformed URL exception";
-        
+
             } else if (mException instanceof UnknownHostException) {
                 return "Unknown host exception";
-        
+
             } else if (mException instanceof CertificateCombinedException) {
                 if (((CertificateCombinedException) mException).isRecoverable())
                     return "SSL recoverable exception";
                 else
                     return "SSL exception";
-                
+
             } else if (mException instanceof SSLException) {
                 return "SSL exception";
 
@@ -239,25 +246,25 @@ public class RemoteOperationResult implements Serializable {
                 return "Unexpected exception";
             }
         }
-        
+
         if (mCode == ResultCode.INSTANCE_NOT_CONFIGURED) {
             return "The ownCloud server is not configured!";
-            
+
         } else if (mCode == ResultCode.NO_NETWORK_CONNECTION) {
             return "No network connection";
-            
+
         } else if (mCode == ResultCode.BAD_OC_VERSION) {
             return "No valid ownCloud version was found at the server";
-            
+
         } else if (mCode == ResultCode.LOCAL_STORAGE_FULL) {
             return "Local storage full";
-            
+
         } else if (mCode == ResultCode.LOCAL_STORAGE_NOT_MOVED) {
             return "Error while moving file to final directory";
         }
-        
-        return "Operation finished with HTTP status code " + mHttpCode + " (" + (isSuccess()?"success":"fail") + ")";
-        
+
+        return "Operation finished with HTTP status code " + mHttpCode + " (" + (isSuccess() ? "success" : "fail") + ")";
+
     }
 
 }
index 80f4097..8348b72 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -21,8 +20,7 @@ package com.owncloud.android.operations;
 import org.apache.commons.httpclient.HttpStatus;
 import org.apache.jackrabbit.webdav.client.methods.DeleteMethod;
 
-import android.util.Log;
-
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.datamodel.DataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 
@@ -91,11 +89,11 @@ public class RemoveFileOperation extends RemoteOperation {
             }
             delete.getResponseBodyAsString();   // exhaust the response, although not interesting
             result = new RemoteOperationResult((delete.succeeded() || status == HttpStatus.SC_NOT_FOUND), status);
-            Log.i(TAG, "Remove " + mFileToRemove.getRemotePath() + ": " + result.getLogMessage());
+            Log_OC.i(TAG, "Remove " + mFileToRemove.getRemotePath() + ": " + result.getLogMessage());
             
         } catch (Exception e) {
             result = new RemoteOperationResult(e);
-            Log.e(TAG, "Remove " + mFileToRemove.getRemotePath() + ": " + result.getLogMessage(), e);
+            Log_OC.e(TAG, "Remove " + mFileToRemove.getRemotePath() + ": " + result.getLogMessage(), e);
             
         } finally {
             if (delete != null)
index b980657..1c636fb 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -25,8 +24,8 @@ import org.apache.jackrabbit.webdav.client.methods.DavMethodBase;
 //import org.apache.jackrabbit.webdav.client.methods.MoveMethod;
 
 import android.accounts.Account;
-import android.util.Log;
 
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.datamodel.DataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
@@ -105,7 +104,7 @@ public class RenameFileOperation extends RemoteOperation {
             }
         
             // check if a file with the new name already exists
-            if (client.existsFile(mNewRemotePath) ||                             // remote check could fail by network failure, or by indeterminate behavior of HEAD for folders ... 
+            if (client.existsFile(mNewRemotePath) ||                             // remote check could fail by network failure. by indeterminate behavior of HEAD for folders ... 
                     mStorageManager.getFileByPath(mNewRemotePath) != null) {     // ... so local check is convenient
                 return new RemoteOperationResult(ResultCode.INVALID_OVERWRITE);
             }
@@ -138,11 +137,11 @@ public class RenameFileOperation extends RemoteOperation {
             
             move.getResponseBodyAsString(); // exhaust response, although not interesting
             result = new RemoteOperationResult(move.succeeded(), status);
-            Log.i(TAG, "Rename " + mFile.getRemotePath() + " to " + mNewRemotePath + ": " + result.getLogMessage());
+            Log_OC.i(TAG, "Rename " + mFile.getRemotePath() + " to " + mNewRemotePath + ": " + result.getLogMessage());
             
         } catch (Exception e) {
             result = new RemoteOperationResult(e);
-            Log.e(TAG, "Rename " + mFile.getRemotePath() + " to " + ((mNewRemotePath==null) ? mNewName : mNewRemotePath) + ": " + result.getLogMessage(), e);
+            Log_OC.e(TAG, "Rename " + mFile.getRemotePath() + " to " + ((mNewRemotePath==null) ? mNewName : mNewRemotePath) + ": " + result.getLogMessage(), e);
             
         } finally {
             if (move != null)
@@ -192,21 +191,27 @@ public class RenameFileOperation extends RemoteOperation {
      * 
      * TODO move this method, and maybe FileDownload.get***Path(), to a class with utilities specific for the interactions with the file system
      * 
-     * @return      'True' if a temporal file named with the name to set could be created in the file system where 
-     *              local files are stored.
+     * @return              'True' if a temporal file named with the name to set could be created in the file system where 
+     *                      local files are stored.
+     * @throws IOException  When the temporal folder can not be created.
      */
-    private boolean isValidNewName() {
+    private boolean isValidNewName() throws IOException {
         // check tricky names
         if (mNewName == null || mNewName.length() <= 0 || mNewName.contains(File.separator) || mNewName.contains("%")) { 
             return false;
         }
         // create a test file
-        String tmpFolder = FileStorageUtils.getTemporalPath("");
-        File testFile = new File(tmpFolder + mNewName);
+        String tmpFolderName = FileStorageUtils.getTemporalPath("");
+        File testFile = new File(tmpFolderName + mNewName);
+        File tmpFolder = testFile.getParentFile();
+        tmpFolder.mkdirs();
+        if (!tmpFolder.isDirectory()) {
+            throw new IOException("Unexpected error: temporal directory could not be created");
+        }
         try {
             testFile.createNewFile();   // return value is ignored; it could be 'false' because the file already existed, that doesn't invalidate the name
         } catch (IOException e) {
-            Log.i(TAG, "Test for validity of name " + mNewName + " in the file system failed");
+            Log_OC.i(TAG, "Test for validity of name " + mNewName + " in the file system failed");
             return false;
         }
         boolean result = (testFile.exists() && testFile.isFile());
index 606017a..b0f2ce2 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -26,8 +25,8 @@ import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
 import android.accounts.Account;
 import android.content.Context;
 import android.content.Intent;
-import android.util.Log;
 
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.datamodel.DataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.services.FileDownloader;
@@ -106,7 +105,7 @@ public class SynchronizeFileOperation extends RemoteOperation {
                     }
                 }
                 
-                if (result == null) {   // true if the server was not checked, or nothing was wrong with the remote request
+                if (result == null) {   // true if the server was not checked. nothing was wrong with the remote request
               
                     /// check changes in server and local file
                     boolean serverChanged = false;
@@ -158,11 +157,11 @@ public class SynchronizeFileOperation extends RemoteOperation {
           
             }
             
-            Log.i(TAG, "Synchronizing " + mAccount.name + ", file " + mLocalFile.getRemotePath() + ": " + result.getLogMessage());
+            Log_OC.i(TAG, "Synchronizing " + mAccount.name + ", file " + mLocalFile.getRemotePath() + ": " + result.getLogMessage());
           
         } catch (Exception e) {
             result = new RemoteOperationResult(e);
-            Log.e(TAG, "Synchronizing " + mAccount.name + ", file " + mLocalFile.getRemotePath() + ": " + result.getLogMessage(), result.getException());
+            Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", file " + (mLocalFile != null ? mLocalFile.getRemotePath() : "NULL") + ": " + result.getLogMessage(), result.getException());
 
         } finally {
             if (propfind != null)
index 977460b..3c1a64f 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -35,8 +34,8 @@ import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
 
 import android.accounts.Account;
 import android.content.Context;
-import android.util.Log;
 
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.datamodel.DataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
@@ -132,7 +131,7 @@ public class SynchronizeFolderOperation extends RemoteOperation {
         // code before in FileSyncAdapter.fetchData
         PropFindMethod query = null;
         try {
-            Log.d(TAG, "Synchronizing " + mAccount.name + ", fetching files in " + mRemotePath);
+            Log_OC.d(TAG, "Synchronizing " + mAccount.name + ", fetching files in " + mRemotePath);
             
             // remote request 
             query = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath));
@@ -209,9 +208,9 @@ public class SynchronizeFolderOperation extends RemoteOperation {
                         } else {
                             mFailsInFavouritesFound++;
                             if (contentsResult.getException() != null) {
-                                Log.d(TAG, "Error while synchronizing favourites : " +  contentsResult.getLogMessage(), contentsResult.getException());
+                                Log_OC.d(TAG, "Error while synchronizing favourites : " +  contentsResult.getLogMessage(), contentsResult.getException());
                             } else {
-                                Log.d(TAG, "Error while synchronizing favourites : " + contentsResult.getLogMessage());
+                                Log_OC.d(TAG, "Error while synchronizing favourites : " + contentsResult.getLogMessage());
                             }
                         }
                     }   // won't let these fails break the synchronization process
@@ -225,7 +224,7 @@ public class SynchronizeFolderOperation extends RemoteOperation {
                 for (int i=0; i < mChildren.size(); ) {
                     file = mChildren.get(i);
                     if (file.getLastSyncDateForProperties() != mCurrentSyncTime) {
-                        Log.d(TAG, "removing file: " + file);
+                        Log_OC.d(TAG, "removing file: " + file);
                         mStorageManager.removeFile(file, (file.isDown() && file.getStoragePath().startsWith(currentSavePath)));
                         mChildren.remove(i);
                     } else {
@@ -248,12 +247,12 @@ public class SynchronizeFolderOperation extends RemoteOperation {
             } else {
                 result = new RemoteOperationResult(false, status);
             }
-            Log.i(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage());
+            Log_OC.i(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage());
             
             
         } catch (Exception e) {
             result = new RemoteOperationResult(e);
-            Log.e(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage(), result.getException());
+            Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage(), result.getException());
 
         } finally {
             if (query != null)
@@ -329,7 +328,7 @@ public class SynchronizeFolderOperation extends RemoteOperation {
                     file.setStoragePath(expectedPath);
                     
                 } catch (Exception e) {
-                    Log.e(TAG, "Exception while copying foreign file " + expectedPath, e);
+                    Log_OC.e(TAG, "Exception while copying foreign file " + expectedPath, e);
                     mForgottenLocalFiles.put(file.getRemotePath(), storagePath);
                     file.setStoragePath(null);
                     
@@ -337,12 +336,12 @@ public class SynchronizeFolderOperation extends RemoteOperation {
                     try {
                         if (in != null) in.close();
                     } catch (Exception e) {
-                        Log.d(TAG, "Weird exception while closing input stream for " + storagePath + " (ignoring)", e);
+                        Log_OC.d(TAG, "Weird exception while closing input stream for " + storagePath + " (ignoring)", e);
                     }
                     try {
                         if (out != null) out.close();
                     } catch (Exception e) {
-                        Log.d(TAG, "Weird exception while closing output stream for " + expectedPath + " (ignoring)", e);
+                        Log_OC.d(TAG, "Weird exception while closing output stream for " + expectedPath + " (ignoring)", e);
                     }
                 }
             }
index 02f79c9..dad5717 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -26,10 +25,10 @@ import org.json.JSONObject;
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.content.Context;
-import android.util.Log;
 
 import com.owncloud.android.AccountUtils;
 import com.owncloud.android.authentication.AccountAuthenticator;
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
 import com.owncloud.android.utils.OwnCloudVersion;
 
@@ -76,11 +75,11 @@ public class UpdateOCVersionOperation extends RemoteOperation {
                         OwnCloudVersion ocver = new OwnCloudVersion(json.getString("version"));
                         if (ocver.isVersionValid()) {
                             accountMngr.setUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION, ocver.toString());
-                            Log.d(TAG, "Got new OC version " + ocver.toString());
+                            Log_OC.d(TAG, "Got new OC version " + ocver.toString());
                             result = new RemoteOperationResult(ResultCode.OK);
                             
                         } else {
-                            Log.w(TAG, "Invalid version number received from server: " + json.getString("version"));
+                            Log_OC.w(TAG, "Invalid version number received from server: " + json.getString("version"));
                             result = new RemoteOperationResult(RemoteOperationResult.ResultCode.BAD_OC_VERSION);
                         }
                     }
@@ -89,15 +88,15 @@ public class UpdateOCVersionOperation extends RemoteOperation {
                     result = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
                 }
             }
-            Log.i(TAG, "Check for update of ownCloud server version at " + client.getBaseUri() + ": " + result.getLogMessage());
+            Log_OC.i(TAG, "Check for update of ownCloud server version at " + client.getBaseUri() + ": " + result.getLogMessage());
             
         } catch (JSONException e) {
             result = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
-            Log.e(TAG, "Check for update of ownCloud server version at " + client.getBaseUri() + ": " + result.getLogMessage(), e);
+            Log_OC.e(TAG, "Check for update of ownCloud server version at " + client.getBaseUri() + ": " + result.getLogMessage(), e);
                 
         } catch (Exception e) {
             result = new RemoteOperationResult(e);
-            Log.e(TAG, "Check for update of ownCloud server version at " + client.getBaseUri() + ": " + result.getLogMessage(), e);
+            Log_OC.e(TAG, "Check for update of ownCloud server version at " + client.getBaseUri() + ": " + result.getLogMessage(), e);
             
         } finally {
             if (get != null) 
index 2824b6d..2c891be 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -30,10 +29,15 @@ import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.commons.httpclient.HttpException;
 import org.apache.commons.httpclient.methods.PutMethod;
+import org.apache.commons.httpclient.methods.RequestEntity;
 import org.apache.http.HttpStatus;
 
+import com.owncloud.android.Log_OC;
+import android.accounts.Account;
+
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.network.ProgressiveDataTransferer;
 import com.owncloud.android.operations.RemoteOperation;
 import com.owncloud.android.operations.RemoteOperationResult;
 import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
@@ -43,8 +47,6 @@ import eu.alefzero.webdav.FileRequestEntity;
 import eu.alefzero.webdav.OnDatatransferProgressListener;
 import eu.alefzero.webdav.WebdavClient;
 import eu.alefzero.webdav.WebdavUtils;
-import android.accounts.Account;
-import android.util.Log;
 
 /**
  * Remote operation performing the upload of a file to an ownCloud server
@@ -52,7 +54,7 @@ import android.util.Log;
  * @author David A. Velasco
  */
 public class UploadFileOperation extends RemoteOperation {
-    
+
     private static final String TAG = UploadFileOperation.class.getSimpleName();
 
     private Account mAccount;
@@ -70,6 +72,8 @@ public class UploadFileOperation extends RemoteOperation {
     private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
     private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
 
+    protected RequestEntity mEntity = null;
+
     
     public UploadFileOperation( Account account,
                                 OCFile file,
@@ -80,10 +84,13 @@ public class UploadFileOperation extends RemoteOperation {
             throw new IllegalArgumentException("Illegal NULL account in UploadFileOperation creation");
         if (file == null)
             throw new IllegalArgumentException("Illegal NULL file in UploadFileOperation creation");
-        if (file.getStoragePath() == null || file.getStoragePath().length() <= 0 || !(new File(file.getStoragePath()).exists())) {
-            throw new IllegalArgumentException("Illegal file in UploadFileOperation; storage path invalid or file not found: " + file.getStoragePath());
+        if (file.getStoragePath() == null || file.getStoragePath().length() <= 0
+                || !(new File(file.getStoragePath()).exists())) {
+            throw new IllegalArgumentException(
+                    "Illegal file in UploadFileOperation; storage path invalid or file not found: "
+                            + file.getStoragePath());
         }
-        
+
         mAccount = account;
         mFile = file;
         mRemotePath = file.getRemotePath();
@@ -94,39 +101,38 @@ public class UploadFileOperation extends RemoteOperation {
         mOriginalFileName = mFile.getFileName();
     }
 
-
     public Account getAccount() {
         return mAccount;
     }
-    
+
     public String getFileName() {
         return mOriginalFileName;
     }
-    
+
     public OCFile getFile() {
         return mFile;
     }
-    
+
     public OCFile getOldFile() {
-        return mOldFile; 
+        return mOldFile;
     }
-    
+
     public String getOriginalStoragePath() {
         return mOriginalStoragePath;
     }
-    
+
     public String getStoragePath() {
         return mFile.getStoragePath();
     }
 
     public String getRemotePath() {
-        return mFile.getRemotePath(); 
+        return mFile.getRemotePath();
     }
 
     public String getMimeType() {
         return mFile.getMimetype();
     }
-    
+
     public boolean isInstant() {
         return mIsInstant;
     }
@@ -134,7 +140,7 @@ public class UploadFileOperation extends RemoteOperation {
     public boolean isRemoteFolderToBeCreated() {
         return mRemoteFolderToBeCreated;
     }
-    
+
     public void setRemoteFolderToBeCreated() {
         mRemoteFolderToBeCreated = true;
     }
@@ -142,50 +148,74 @@ public class UploadFileOperation extends RemoteOperation {
     public boolean getForceOverwrite() {
         return mForceOverwrite;
     }
-    
+
     public boolean wasRenamed() {
         return mWasRenamed;
     }
-    
+
     public Set<OnDatatransferProgressListener> getDataTransferListeners() {
         return mDataTransferListeners;
     }
     
     public void addDatatransferProgressListener (OnDatatransferProgressListener listener) {
-        mDataTransferListeners.add(listener);
+        synchronized (mDataTransferListeners) {
+            mDataTransferListeners.add(listener);
+        }
+        if (mEntity != null) {
+            ((ProgressiveDataTransferer)mEntity).addDatatransferProgressListener(listener);
+        }
     }
     
+    public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) {
+        synchronized (mDataTransferListeners) {
+            mDataTransferListeners.remove(listener);
+        }
+        if (mEntity != null) {
+            ((ProgressiveDataTransferer)mEntity).removeDatatransferProgressListener(listener);
+        }
+    }
+
     @Override
     protected RemoteOperationResult run(WebdavClient client) {
         RemoteOperationResult result = null;
         boolean localCopyPassed = false, nameCheckPassed = false;
         File temporalFile = null, originalFile = new File(mOriginalStoragePath), expectedFile = null;
         try {
-            /// rename the file to upload, if necessary
+            // / rename the file to upload, if necessary
             if (!mForceOverwrite) {
                 String remotePath = getAvailableRemotePath(client, mRemotePath);
                 mWasRenamed = !remotePath.equals(mRemotePath);
                 if (mWasRenamed) {
-                   createNewOCFile(remotePath);
+                    createNewOCFile(remotePath);
                 }
             }
             nameCheckPassed = true;
-        
-            String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile);  /// not before getAvailableRemotePath() !!!
+
+            String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile); // /
+                                                                                                // not
+                                                                                                // before
+                                                                                                // getAvailableRemotePath()
+                                                                                                // !!!
             expectedFile = new File(expectedPath);
-            
-            /// check location of local file; if not the expected, copy to a temporal file before upload (if COPY is the expected behaviour)
+
+            // / check location of local file; if not the expected, copy to a
+            // temporal file before upload (if COPY is the expected behaviour)
             if (!mOriginalStoragePath.equals(expectedPath) && mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_COPY) {
 
                 if (FileStorageUtils.getUsableSpace(mAccount.name) < originalFile.length()) {
                     result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_FULL);
-                    return result;  // error condition when the file should be copied
-                        
+                    return result; // error condition when the file should be
+                                   // copied
+
                 } else {
                     String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath();
                     mFile.setStoragePath(temporalPath);
                     temporalFile = new File(temporalPath);
-                    if (!mOriginalStoragePath.equals(temporalPath)) {   // preventing weird but possible situation
+                    if (!mOriginalStoragePath.equals(temporalPath)) { // preventing
+                                                                      // weird
+                                                                      // but
+                                                                      // possible
+                                                                      // situation
                         InputStream in = null;
                         OutputStream out = null;
                         try {
@@ -197,38 +227,40 @@ public class UploadFileOperation extends RemoteOperation {
                             temporalFile.createNewFile();
                             if (!temporalFile.isFile()) {
                                 throw new IOException("Unexpected error: target file could not be created");
-                            }                    
+                            }
                             in = new FileInputStream(originalFile);
                             out = new FileOutputStream(temporalFile);
                             byte[] buf = new byte[1024];
                             int len;
-                            while ((len = in.read(buf)) > 0){
+                            while ((len = in.read(buf)) > 0) {
                                 out.write(buf, 0, len);
                             }
-                            
+
                         } catch (Exception e) {
                             result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_COPIED);
                             return result;
-                            
+
                         } finally {
                             try {
-                                if (in != null) in.close();
+                                if (in != null)
+                                    in.close();
                             } catch (Exception e) {
-                                Log.d(TAG, "Weird exception while closing input stream for " + mOriginalStoragePath + " (ignoring)", e);
+                                Log_OC.d(TAG, "Weird exception while closing input stream for " + mOriginalStoragePath + " (ignoring)", e);
                             }
                             try {
-                                if (out != null) out.close();
+                                if (out != null)
+                                    out.close();
                             } catch (Exception e) {
-                                Log.d(TAG, "Weird exception while closing output stream for " + expectedPath + " (ignoring)", e);
+                                Log_OC.d(TAG, "Weird exception while closing output stream for " + expectedPath + " (ignoring)", e);
                             }
                         }
                     }
                 }
             }
             localCopyPassed = true;
-            
-            /// perform the upload
-            synchronized(mCancellationRequested) {
+
+            // / perform the upload
+            synchronized (mCancellationRequested) {
                 if (mCancellationRequested.get()) {
                     throw new OperationCancelledException();
                 } else {
@@ -236,19 +268,21 @@ public class UploadFileOperation extends RemoteOperation {
                 }
             }
             int status = uploadFile(client);
-            
-            
-            /// move local temporal file or original file to its corresponding location in the ownCloud local folder
+
+            // / move local temporal file or original file to its corresponding
+            // location in the ownCloud local folder
             if (isSuccess(status)) {
                 if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_FORGET) {
                     mFile.setStoragePath(null);
-                    
+
                 } else {
                     mFile.setStoragePath(expectedPath);
                     File fileToMove = null;
-                    if (temporalFile != null) {             // FileUploader.LOCAL_BEHAVIOUR_COPY ; see where temporalFile was set
+                    if (temporalFile != null) { // FileUploader.LOCAL_BEHAVIOUR_COPY
+                                                // ; see where temporalFile was
+                                                // set
                         fileToMove = temporalFile;
-                    } else {                                // FileUploader.LOCAL_BEHAVIOUR_MOVE
+                    } else { // FileUploader.LOCAL_BEHAVIOUR_MOVE
                         fileToMove = originalFile;
                     }
                     if (!expectedFile.equals(fileToMove)) {
@@ -256,18 +290,22 @@ public class UploadFileOperation extends RemoteOperation {
                         expectedFolder.mkdirs();
                         if (!expectedFolder.isDirectory() || !fileToMove.renameTo(expectedFile)) {
                             mFile.setStoragePath(null); // forget the local file
-                            // by now, treat this as a success; the file was uploaded; the user won't like that the local file is not linked, but this should be a veeery rare fail;
-                            // the best option could be show a warning message (but not a fail)
-                            //result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_MOVED);
-                            //return result;
+                            // by now, treat this as a success; the file was
+                            // uploaded; the user won't like that the local file
+                            // is not linked, but this should be a very rare
+                            // fail;
+                            // the best option could be show a warning message
+                            // (but not a fail)
+                            // result = new
+                            // RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_MOVED);
+                            // return result;
                         }
                     }
-                } 
+                }
             }
-            
+
             result = new RemoteOperationResult(isSuccess(status), status);
-            
-            
+
         } catch (Exception e) {
             // TODO something cleaner with cancellations
             if (mCancellationRequested.get()) {
@@ -275,34 +313,32 @@ public class UploadFileOperation extends RemoteOperation {
             } else {
                 result = new RemoteOperationResult(e);
             }
-            
-            
+
         } finally {
             if (temporalFile != null && !originalFile.equals(temporalFile)) {
                 temporalFile.delete();
             }
             if (result.isSuccess()) {
-                Log.i(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage());
-                    
+                Log_OC.i(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage());
             } else {
                 if (result.getException() != null) {
                     String complement = "";
                     if (!nameCheckPassed) {
                         complement = " (while checking file existence in server)";
                     } else if (!localCopyPassed) {
-                        complement = " (while copying local file to " + FileStorageUtils.getSavePath(mAccount.name) + ")";
+                        complement = " (while copying local file to " + FileStorageUtils.getSavePath(mAccount.name)
+                                + ")";
                     }
-                    Log.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage() + complement, result.getException());
+                    Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage() + complement, result.getException());
                 } else {
-                    Log.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage());
+                    Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage());
                 }
             }
         }
-        
+
         return result;
     }
 
-    
     private void createNewOCFile(String newRemotePath) {
         // a new OCFile instance must be created for a new remote path
         OCFile newFile = new OCFile(newRemotePath);
@@ -321,31 +357,32 @@ public class UploadFileOperation extends RemoteOperation {
         mFile = newFile;
     }
 
-
     public boolean isSuccess(int status) {
         return ((status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED || status == HttpStatus.SC_NO_CONTENT));
     }
-    
-    
+
     protected int uploadFile(WebdavClient client) throws HttpException, IOException, OperationCancelledException {
         int status = -1;
         try {
             File f = new File(mFile.getStoragePath());
-            FileRequestEntity entity = new FileRequestEntity(f, getMimeType());
-            entity.addOnDatatransferProgressListeners(mDataTransferListeners);
-            mPutMethod.setRequestEntity(entity);
+            mEntity  = new FileRequestEntity(f, getMimeType());
+            synchronized (mDataTransferListeners) {
+                ((ProgressiveDataTransferer)mEntity).addDatatransferProgressListeners(mDataTransferListeners);
+            }
+            mPutMethod.setRequestEntity(mEntity);
             status = client.executeMethod(mPutMethod);
             client.exhaustResponse(mPutMethod.getResponseBodyAsStream());
-            
+
         } finally {
-            mPutMethod.releaseConnection();    // let the connection available for other methods
+            mPutMethod.releaseConnection(); // let the connection available for
+                                            // other methods
         }
         return status;
     }
-    
+
     /**
-     * Checks if remotePath does not exist in the server and returns it, or adds a suffix to it in order to avoid the server
-     * file is overwritten.
+     * Checks if remotePath does not exist in the server and returns it, or adds
+     * a suffix to it in order to avoid the server file is overwritten.
      * 
      * @param string
      * @return
@@ -355,12 +392,12 @@ public class UploadFileOperation extends RemoteOperation {
         if (!check) {
             return remotePath;
         }
-    
+
         int pos = remotePath.lastIndexOf(".");
         String suffix = "";
         String extension = "";
         if (pos >= 0) {
-            extension = remotePath.substring(pos+1);
+            extension = remotePath.substring(pos + 1);
             remotePath = remotePath.substring(0, pos);
         }
         int count = 2;
@@ -373,21 +410,19 @@ public class UploadFileOperation extends RemoteOperation {
             count++;
         } while (check);
 
-        if (pos >=0) {
+        if (pos >= 0) {
             return remotePath + suffix + "." + extension;
         } else {
             return remotePath + suffix;
         }
     }
 
-
     public void cancel() {
-        synchronized(mCancellationRequested) {
+        synchronized (mCancellationRequested) {
             mCancellationRequested.set(true);
             if (mPutMethod != null)
                 mPutMethod.abort();
         }
     }
 
-
 }
index 5e54341..9b03243 100644 (file)
-/* ownCloud Android client application\r
- *   Copyright (C) 2011  Bartek Przybylski\r
- *   Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
- *\r
- *   This program is distributed in the hope that it will be useful,\r
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- *   GNU General Public License for more details.\r
- *\r
- *   You should have received a copy of the GNU General Public License\r
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-\r
-package com.owncloud.android.providers;\r
-\r
-import java.util.HashMap;\r
-\r
-import com.owncloud.android.db.ProviderMeta;\r
-import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;\r
-\r
-\r
-import android.content.ContentProvider;\r
-import android.content.ContentUris;\r
-import android.content.ContentValues;\r
-import android.content.Context;\r
-import android.content.UriMatcher;\r
-import android.database.Cursor;\r
-import android.database.SQLException;\r
-import android.database.sqlite.SQLiteDatabase;\r
-import android.database.sqlite.SQLiteOpenHelper;\r
-import android.database.sqlite.SQLiteQueryBuilder;\r
-import android.net.Uri;\r
-import android.text.TextUtils;\r
-import android.util.Log;\r
-\r
-/**\r
- * The ContentProvider for the ownCloud App.\r
- * \r
- * @author Bartek Przybylski\r
- * \r
- */\r
-public class FileContentProvider extends ContentProvider {\r
-\r
-    private DataBaseHelper mDbHelper;\r
-\r
-    private static HashMap<String, String> mProjectionMap;\r
-    static {\r
-        mProjectionMap = new HashMap<String, String>();\r
-        mProjectionMap.put(ProviderTableMeta._ID, ProviderTableMeta._ID);\r
-        mProjectionMap.put(ProviderTableMeta.FILE_PARENT,\r
-                ProviderTableMeta.FILE_PARENT);\r
-        mProjectionMap.put(ProviderTableMeta.FILE_PATH,\r
-                ProviderTableMeta.FILE_PATH);\r
-        mProjectionMap.put(ProviderTableMeta.FILE_NAME,\r
-                ProviderTableMeta.FILE_NAME);\r
-        mProjectionMap.put(ProviderTableMeta.FILE_CREATION,\r
-                ProviderTableMeta.FILE_CREATION);\r
-        mProjectionMap.put(ProviderTableMeta.FILE_MODIFIED,\r
-                ProviderTableMeta.FILE_MODIFIED);\r
-        mProjectionMap.put(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA,\r
-                ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA);\r
-        mProjectionMap.put(ProviderTableMeta.FILE_CONTENT_LENGTH,\r
-                ProviderTableMeta.FILE_CONTENT_LENGTH);\r
-        mProjectionMap.put(ProviderTableMeta.FILE_CONTENT_TYPE,\r
-                ProviderTableMeta.FILE_CONTENT_TYPE);\r
-        mProjectionMap.put(ProviderTableMeta.FILE_STORAGE_PATH,\r
-                ProviderTableMeta.FILE_STORAGE_PATH);\r
-        mProjectionMap.put(ProviderTableMeta.FILE_LAST_SYNC_DATE,\r
-                ProviderTableMeta.FILE_LAST_SYNC_DATE);\r
-        mProjectionMap.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA,\r
-                ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA);\r
-        mProjectionMap.put(ProviderTableMeta.FILE_KEEP_IN_SYNC,\r
-                ProviderTableMeta.FILE_KEEP_IN_SYNC);\r
-        mProjectionMap.put(ProviderTableMeta.FILE_ACCOUNT_OWNER,\r
-                ProviderTableMeta.FILE_ACCOUNT_OWNER);\r
-    }\r
-\r
-    private static final int SINGLE_FILE = 1;\r
-    private static final int DIRECTORY = 2;\r
-    private static final int ROOT_DIRECTORY = 3;\r
-    private static final UriMatcher mUriMatcher;\r
-    static {\r
-        mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);\r
-        mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, "/", ROOT_DIRECTORY);\r
-        mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, "file/", SINGLE_FILE);\r
-        mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, "file/#", SINGLE_FILE);\r
-        mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, "dir/#", DIRECTORY);\r
-    }\r
-\r
-    @Override\r
-    public int delete(Uri uri, String where, String[] whereArgs) {\r
-        SQLiteDatabase db = mDbHelper.getWritableDatabase();\r
-        int count = 0;\r
-        switch (mUriMatcher.match(uri)) {\r
-        case SINGLE_FILE:\r
-            count = db.delete(ProviderTableMeta.DB_NAME,\r
-                    ProviderTableMeta._ID\r
-                            + "="\r
-                            + uri.getPathSegments().get(1)\r
-                            + (!TextUtils.isEmpty(where) ? " AND (" + where\r
-                                    + ")" : ""), whereArgs);\r
-            break;\r
-        case ROOT_DIRECTORY:\r
-            count = db.delete(ProviderTableMeta.DB_NAME, where, whereArgs);\r
-            break;\r
-        default:\r
-            throw new IllegalArgumentException("Unknown uri: " + uri.toString());\r
-        }\r
-        getContext().getContentResolver().notifyChange(uri, null);\r
-        return count;\r
-    }\r
-\r
-    @Override\r
-    public String getType(Uri uri) {\r
-        switch (mUriMatcher.match(uri)) {\r
-        case ROOT_DIRECTORY:\r
-            return ProviderTableMeta.CONTENT_TYPE;\r
-        case SINGLE_FILE:\r
-            return ProviderTableMeta.CONTENT_TYPE_ITEM;\r
-        default:\r
-            throw new IllegalArgumentException("Unknown Uri id."\r
-                    + uri.toString());\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public Uri insert(Uri uri, ContentValues values) {\r
-        if (mUriMatcher.match(uri) != SINGLE_FILE &&\r
-            mUriMatcher.match(uri) != ROOT_DIRECTORY) {\r
-            \r
-            throw new IllegalArgumentException("Unknown uri id: " + uri);\r
-        }\r
-\r
-        SQLiteDatabase db = mDbHelper.getWritableDatabase();\r
-        long rowId = db.insert(ProviderTableMeta.DB_NAME, null, values);\r
-        if (rowId > 0) {\r
-            Uri insertedFileUri = ContentUris.withAppendedId(\r
-                    ProviderTableMeta.CONTENT_URI_FILE, rowId);\r
-            getContext().getContentResolver().notifyChange(insertedFileUri,\r
-                    null);\r
-            return insertedFileUri;\r
-        }\r
-        throw new SQLException("ERROR " + uri);\r
-    }\r
-\r
-    @Override\r
-    public boolean onCreate() {\r
-        mDbHelper = new DataBaseHelper(getContext());\r
-        return true;\r
-    }\r
-\r
-    @Override\r
-    public Cursor query(Uri uri, String[] projection, String selection,\r
-            String[] selectionArgs, String sortOrder) {\r
-        SQLiteQueryBuilder sqlQuery = new SQLiteQueryBuilder();\r
-\r
-        sqlQuery.setTables(ProviderTableMeta.DB_NAME);\r
-        sqlQuery.setProjectionMap(mProjectionMap);\r
-\r
-        switch (mUriMatcher.match(uri)) {\r
-        case ROOT_DIRECTORY:\r
-            break;\r
-        case DIRECTORY:\r
-            sqlQuery.appendWhere(ProviderTableMeta.FILE_PARENT + "="\r
-                    + uri.getPathSegments().get(1));\r
-            break;\r
-        case SINGLE_FILE:\r
-            if (uri.getPathSegments().size() > 1) {\r
-                sqlQuery.appendWhere(ProviderTableMeta._ID + "="\r
-                        + uri.getPathSegments().get(1));\r
-            }\r
-            break;\r
-        default:\r
-            throw new IllegalArgumentException("Unknown uri id: " + uri);\r
-        }\r
-\r
-        String order;\r
-        if (TextUtils.isEmpty(sortOrder)) {\r
-            order = ProviderTableMeta.DEFAULT_SORT_ORDER;\r
-        } else {\r
-            order = sortOrder;\r
-        }\r
-\r
-        SQLiteDatabase db = mDbHelper.getReadableDatabase();\r
-        Cursor c = sqlQuery.query(db, projection, selection, selectionArgs,\r
-                null, null, order);\r
-\r
-        c.setNotificationUri(getContext().getContentResolver(), uri);\r
-\r
-        return c;\r
-    }\r
-\r
-    @Override\r
-    public int update(Uri uri, ContentValues values, String selection,\r
-            String[] selectionArgs) {\r
-        return mDbHelper.getWritableDatabase().update(\r
-                ProviderTableMeta.DB_NAME, values, selection, selectionArgs);\r
-    }\r
-\r
-    class DataBaseHelper extends SQLiteOpenHelper {\r
-\r
-        public DataBaseHelper(Context context) {\r
-            super(context, ProviderMeta.DB_NAME, null, ProviderMeta.DB_VERSION);\r
-\r
-        }\r
-\r
-        @Override\r
-        public void onCreate(SQLiteDatabase db) {\r
-            // files table\r
-            Log.i("SQL", "Entering in onCreate");\r
-            db.execSQL("CREATE TABLE " + ProviderTableMeta.DB_NAME + "("\r
-                    + ProviderTableMeta._ID + " INTEGER PRIMARY KEY, "\r
-                    + ProviderTableMeta.FILE_NAME + " TEXT, "\r
-                    + ProviderTableMeta.FILE_PATH + " TEXT, "\r
-                    + ProviderTableMeta.FILE_PARENT + " INTEGER, "\r
-                    + ProviderTableMeta.FILE_CREATION + " INTEGER, "\r
-                    + ProviderTableMeta.FILE_MODIFIED + " INTEGER, "\r
-                    + ProviderTableMeta.FILE_CONTENT_TYPE + " TEXT, "\r
-                    + ProviderTableMeta.FILE_CONTENT_LENGTH + " INTEGER, "\r
-                    + ProviderTableMeta.FILE_STORAGE_PATH + " TEXT, "\r
-                    + ProviderTableMeta.FILE_ACCOUNT_OWNER + " TEXT, "\r
-                    + ProviderTableMeta.FILE_LAST_SYNC_DATE + " INTEGER, "\r
-                    + ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER, "\r
-                    + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " INTEGER, "\r
-                    + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " INTEGER );"\r
-                    );\r
-        }\r
-\r
-        @Override\r
-        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {\r
-            Log.i("SQL", "Entering in onUpgrade");\r
-            boolean upgraded = false; \r
-            if (oldVersion == 1 && newVersion >= 2) {\r
-                Log.i("SQL", "Entering in the #1 ADD in onUpgrade");\r
-                db.execSQL("ALTER TABLE " + ProviderTableMeta.DB_NAME +\r
-                           " ADD COLUMN " + ProviderTableMeta.FILE_KEEP_IN_SYNC  + " INTEGER " +\r
-                           " DEFAULT 0");\r
-                upgraded = true;\r
-            }\r
-            if (oldVersion < 3 && newVersion >= 3) {\r
-                Log.i("SQL", "Entering in the #2 ADD in onUpgrade");\r
-                db.beginTransaction();\r
-                try {\r
-                    db.execSQL("ALTER TABLE " + ProviderTableMeta.DB_NAME +\r
-                               " ADD COLUMN " + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA  + " INTEGER " +\r
-                               " DEFAULT 0");\r
-                    \r
-                    // assume there are not local changes pending to upload\r
-                    db.execSQL("UPDATE " + ProviderTableMeta.DB_NAME + \r
-                            " SET " + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " = " + System.currentTimeMillis() + \r
-                            " WHERE " + ProviderTableMeta.FILE_STORAGE_PATH + " IS NOT NULL");\r
-                 \r
-                    upgraded = true;\r
-                    db.setTransactionSuccessful();\r
-                } finally {\r
-                    db.endTransaction();\r
-                }\r
-            }\r
-            if (oldVersion < 4 && newVersion >= 4) {\r
-                Log.i("SQL", "Entering in the #3 ADD in onUpgrade");\r
-                db.beginTransaction();\r
-                try {\r
-                    db .execSQL("ALTER TABLE " + ProviderTableMeta.DB_NAME +\r
-                           " ADD COLUMN " + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA  + " INTEGER " +\r
-                           " DEFAULT 0");\r
-                \r
-                    db.execSQL("UPDATE " + ProviderTableMeta.DB_NAME + \r
-                           " SET " + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " = " + ProviderTableMeta.FILE_MODIFIED + \r
-                           " WHERE " + ProviderTableMeta.FILE_STORAGE_PATH + " IS NOT NULL");\r
-                \r
-                    upgraded = true;\r
-                    db.setTransactionSuccessful();\r
-                } finally {\r
-                    db.endTransaction();\r
-                }\r
-            }\r
-            if (!upgraded)\r
-                Log.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + ", newVersion == " + newVersion);\r
-        }\r
-\r
-    }\r
-\r
-}\r
+/* ownCloud Android client application
+ *   Copyright (C) 2011  Bartek Przybylski
+ *   Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License 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.providers;
+
+import java.util.HashMap;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.db.ProviderMeta;
+import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
+
+
+import android.content.ContentProvider;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.net.Uri;
+import android.text.TextUtils;
+
+/**
+ * The ContentProvider for the ownCloud App.
+ * 
+ * @author Bartek Przybylski
+ * 
+ */
+public class FileContentProvider extends ContentProvider {
+
+    private DataBaseHelper mDbHelper;
+
+    private static HashMap<String, String> mProjectionMap;
+    static {
+        mProjectionMap = new HashMap<String, String>();
+        mProjectionMap.put(ProviderTableMeta._ID, ProviderTableMeta._ID);
+        mProjectionMap.put(ProviderTableMeta.FILE_PARENT,
+                ProviderTableMeta.FILE_PARENT);
+        mProjectionMap.put(ProviderTableMeta.FILE_PATH,
+                ProviderTableMeta.FILE_PATH);
+        mProjectionMap.put(ProviderTableMeta.FILE_NAME,
+                ProviderTableMeta.FILE_NAME);
+        mProjectionMap.put(ProviderTableMeta.FILE_CREATION,
+                ProviderTableMeta.FILE_CREATION);
+        mProjectionMap.put(ProviderTableMeta.FILE_MODIFIED,
+                ProviderTableMeta.FILE_MODIFIED);
+        mProjectionMap.put(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA,
+                ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA);
+        mProjectionMap.put(ProviderTableMeta.FILE_CONTENT_LENGTH,
+                ProviderTableMeta.FILE_CONTENT_LENGTH);
+        mProjectionMap.put(ProviderTableMeta.FILE_CONTENT_TYPE,
+                ProviderTableMeta.FILE_CONTENT_TYPE);
+        mProjectionMap.put(ProviderTableMeta.FILE_STORAGE_PATH,
+                ProviderTableMeta.FILE_STORAGE_PATH);
+        mProjectionMap.put(ProviderTableMeta.FILE_LAST_SYNC_DATE,
+                ProviderTableMeta.FILE_LAST_SYNC_DATE);
+        mProjectionMap.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA,
+                ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA);
+        mProjectionMap.put(ProviderTableMeta.FILE_KEEP_IN_SYNC,
+                ProviderTableMeta.FILE_KEEP_IN_SYNC);
+        mProjectionMap.put(ProviderTableMeta.FILE_ACCOUNT_OWNER,
+                ProviderTableMeta.FILE_ACCOUNT_OWNER);
+    }
+
+    private static final int SINGLE_FILE = 1;
+    private static final int DIRECTORY = 2;
+    private static final int ROOT_DIRECTORY = 3;
+    private static final UriMatcher mUriMatcher;
+    static {
+        mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+        mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, "/", ROOT_DIRECTORY);
+        mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, "file/", SINGLE_FILE);
+        mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, "file/#", SINGLE_FILE);
+        mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, "dir/#", DIRECTORY);
+    }
+
+    @Override
+    public int delete(Uri uri, String where, String[] whereArgs) {
+        SQLiteDatabase db = mDbHelper.getWritableDatabase();
+        int count = 0;
+        switch (mUriMatcher.match(uri)) {
+        case SINGLE_FILE:
+            count = db.delete(ProviderTableMeta.DB_NAME,
+                    ProviderTableMeta._ID
+                            + "="
+                            + uri.getPathSegments().get(1)
+                            + (!TextUtils.isEmpty(where) ? " AND (" + where
+                                    + ")" : ""), whereArgs);
+            break;
+        case ROOT_DIRECTORY:
+            count = db.delete(ProviderTableMeta.DB_NAME, where, whereArgs);
+            break;
+        default:
+            throw new IllegalArgumentException("Unknown uri: " + uri.toString());
+        }
+        getContext().getContentResolver().notifyChange(uri, null);
+        return count;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        switch (mUriMatcher.match(uri)) {
+        case ROOT_DIRECTORY:
+            return ProviderTableMeta.CONTENT_TYPE;
+        case SINGLE_FILE:
+            return ProviderTableMeta.CONTENT_TYPE_ITEM;
+        default:
+            throw new IllegalArgumentException("Unknown Uri id."
+                    + uri.toString());
+        }
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        if (mUriMatcher.match(uri) != SINGLE_FILE &&
+            mUriMatcher.match(uri) != ROOT_DIRECTORY) {
+            
+            throw new IllegalArgumentException("Unknown uri id: " + uri);
+        }
+
+        SQLiteDatabase db = mDbHelper.getWritableDatabase();
+        long rowId = db.insert(ProviderTableMeta.DB_NAME, null, values);
+        if (rowId > 0) {
+            Uri insertedFileUri = ContentUris.withAppendedId(
+                    ProviderTableMeta.CONTENT_URI_FILE, rowId);
+            getContext().getContentResolver().notifyChange(insertedFileUri,
+                    null);
+            return insertedFileUri;
+        }
+        throw new SQLException("ERROR " + uri);
+    }
+
+    @Override
+    public boolean onCreate() {
+        mDbHelper = new DataBaseHelper(getContext());
+        return true;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection,
+            String[] selectionArgs, String sortOrder) {
+        SQLiteQueryBuilder sqlQuery = new SQLiteQueryBuilder();
+
+        sqlQuery.setTables(ProviderTableMeta.DB_NAME);
+        sqlQuery.setProjectionMap(mProjectionMap);
+
+        switch (mUriMatcher.match(uri)) {
+        case ROOT_DIRECTORY:
+            break;
+        case DIRECTORY:
+            sqlQuery.appendWhere(ProviderTableMeta.FILE_PARENT + "="
+                    + uri.getPathSegments().get(1));
+            break;
+        case SINGLE_FILE:
+            if (uri.getPathSegments().size() > 1) {
+                sqlQuery.appendWhere(ProviderTableMeta._ID + "="
+                        + uri.getPathSegments().get(1));
+            }
+            break;
+        default:
+            throw new IllegalArgumentException("Unknown uri id: " + uri);
+        }
+
+        String order;
+        if (TextUtils.isEmpty(sortOrder)) {
+            order = ProviderTableMeta.DEFAULT_SORT_ORDER;
+        } else {
+            order = sortOrder;
+        }
+
+        SQLiteDatabase db = mDbHelper.getReadableDatabase();
+        Cursor c = sqlQuery.query(db, projection, selection, selectionArgs,
+                null, null, order);
+
+        c.setNotificationUri(getContext().getContentResolver(), uri);
+
+        return c;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection,
+            String[] selectionArgs) {
+        return mDbHelper.getWritableDatabase().update(
+                ProviderTableMeta.DB_NAME, values, selection, selectionArgs);
+    }
+
+    class DataBaseHelper extends SQLiteOpenHelper {
+
+        public DataBaseHelper(Context context) {
+            super(context, ProviderMeta.DB_NAME, null, ProviderMeta.DB_VERSION);
+
+        }
+
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+            // files table
+            Log_OC.i("SQL", "Entering in onCreate");
+            db.execSQL("CREATE TABLE " + ProviderTableMeta.DB_NAME + "("
+                    + ProviderTableMeta._ID + " INTEGER PRIMARY KEY, "
+                    + ProviderTableMeta.FILE_NAME + " TEXT, "
+                    + ProviderTableMeta.FILE_PATH + " TEXT, "
+                    + ProviderTableMeta.FILE_PARENT + " INTEGER, "
+                    + ProviderTableMeta.FILE_CREATION + " INTEGER, "
+                    + ProviderTableMeta.FILE_MODIFIED + " INTEGER, "
+                    + ProviderTableMeta.FILE_CONTENT_TYPE + " TEXT, "
+                    + ProviderTableMeta.FILE_CONTENT_LENGTH + " INTEGER, "
+                    + ProviderTableMeta.FILE_STORAGE_PATH + " TEXT, "
+                    + ProviderTableMeta.FILE_ACCOUNT_OWNER + " TEXT, "
+                    + ProviderTableMeta.FILE_LAST_SYNC_DATE + " INTEGER, "
+                    + ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER, "
+                    + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " INTEGER, "
+                    + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " INTEGER );"
+                    );
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            Log_OC.i("SQL", "Entering in onUpgrade");
+            boolean upgraded = false; 
+            if (oldVersion == 1 && newVersion >= 2) {
+                Log_OC.i("SQL", "Entering in the #1 ADD in onUpgrade");
+                db.execSQL("ALTER TABLE " + ProviderTableMeta.DB_NAME +
+                           " ADD COLUMN " + ProviderTableMeta.FILE_KEEP_IN_SYNC  + " INTEGER " +
+                           " DEFAULT 0");
+                upgraded = true;
+            }
+            if (oldVersion < 3 && newVersion >= 3) {
+                Log_OC.i("SQL", "Entering in the #2 ADD in onUpgrade");
+                db.beginTransaction();
+                try {
+                    db.execSQL("ALTER TABLE " + ProviderTableMeta.DB_NAME +
+                               " ADD COLUMN " + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA  + " INTEGER " +
+                               " DEFAULT 0");
+                    
+                    // assume there are not local changes pending to upload
+                    db.execSQL("UPDATE " + ProviderTableMeta.DB_NAME + 
+                            " SET " + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " = " + System.currentTimeMillis() + 
+                            " WHERE " + ProviderTableMeta.FILE_STORAGE_PATH + " IS NOT NULL");
+                 
+                    upgraded = true;
+                    db.setTransactionSuccessful();
+                } finally {
+                    db.endTransaction();
+                }
+            }
+            if (oldVersion < 4 && newVersion >= 4) {
+                Log_OC.i("SQL", "Entering in the #3 ADD in onUpgrade");
+                db.beginTransaction();
+                try {
+                    db .execSQL("ALTER TABLE " + ProviderTableMeta.DB_NAME +
+                           " ADD COLUMN " + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA  + " INTEGER " +
+                           " DEFAULT 0");
+                
+                    db.execSQL("UPDATE " + ProviderTableMeta.DB_NAME + 
+                           " SET " + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " = " + ProviderTableMeta.FILE_MODIFIED + 
+                           " WHERE " + ProviderTableMeta.FILE_STORAGE_PATH + " IS NOT NULL");
+                
+                    upgraded = true;
+                    db.setTransactionSuccessful();
+                } finally {
+                    db.endTransaction();
+                }
+            }
+            if (!upgraded)
+                Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + ", newVersion == " + newVersion);
+        }
+
+    }
+
+}
index b22fdf7..469ee3b 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.\r
  *\r
  *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
+ *   it under the terms of the GNU General Public License version 2,\r
+ *   as published by the Free Software Foundation.\r
  *\r
  *   This program is distributed in the hope that it will be useful,\r
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index 0b0adae..b82a400 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index 7c702d9..6d7c46c 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index c3370f2..ec8750c 100644 (file)
-/* ownCloud Android client application\r
- *   Copyright (C) 2011  Bartek Przybylski\r
- *   Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
- *\r
- *   This program is distributed in the hope that it will be useful,\r
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- *   GNU General Public License for more details.\r
- *\r
- *   You should have received a copy of the GNU General Public License\r
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-\r
-package com.owncloud.android.syncadapter;\r
-\r
-import java.io.IOException;\r
-import java.util.ArrayList;\r
-import java.util.HashMap;\r
-import java.util.List;\r
-import java.util.Map;\r
-\r
-import org.apache.jackrabbit.webdav.DavException;\r
-\r
-import com.owncloud.android.R;\r
-import com.owncloud.android.authentication.AuthenticatorActivity;\r
-import com.owncloud.android.datamodel.DataStorageManager;\r
-import com.owncloud.android.datamodel.FileDataStorageManager;\r
-import com.owncloud.android.datamodel.OCFile;\r
-import com.owncloud.android.operations.RemoteOperationResult;\r
-import com.owncloud.android.operations.SynchronizeFolderOperation;\r
-import com.owncloud.android.operations.UpdateOCVersionOperation;\r
-import com.owncloud.android.operations.RemoteOperationResult.ResultCode;\r
-import com.owncloud.android.ui.activity.ErrorsWhileCopyingHandlerActivity;\r
-\r
-import android.accounts.Account;\r
-import android.accounts.AccountsException;\r
-import android.app.Notification;\r
-import android.app.NotificationManager;\r
-import android.app.PendingIntent;\r
-import android.content.ContentProviderClient;\r
-import android.content.ContentResolver;\r
-import android.content.Context;\r
-import android.content.Intent;\r
-import android.content.SyncResult;\r
-import android.os.Bundle;\r
-import android.util.Log;\r
-\r
-/**\r
- * SyncAdapter implementation for syncing sample SyncAdapter contacts to the\r
- * platform ContactOperations provider.\r
- * \r
- * @author Bartek Przybylski\r
- */\r
-public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {\r
-\r
-    private final static String TAG = "FileSyncAdapter";\r
-\r
-    /** \r
-     * Maximum number of failed folder synchronizations that are supported before finishing the synchronization operation\r
-     */\r
-    private static final int MAX_FAILED_RESULTS = 3; \r
-    \r
-    private long mCurrentSyncTime;\r
-    private boolean mCancellation;\r
-    private boolean mIsManualSync;\r
-    private int mFailedResultsCounter;    \r
-    private RemoteOperationResult mLastFailedResult;\r
-    private SyncResult mSyncResult;\r
-    private int mConflictsFound;\r
-    private int mFailsInFavouritesFound;\r
-    private Map<String, String> mForgottenLocalFiles;\r
-\r
-    \r
-    public FileSyncAdapter(Context context, boolean autoInitialize) {\r
-        super(context, autoInitialize);\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     */\r
-    @Override\r
-    public synchronized void onPerformSync(Account account, Bundle extras,\r
-            String authority, ContentProviderClient provider,\r
-            SyncResult syncResult) {\r
-\r
-        mCancellation = false;\r
-        mIsManualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);\r
-        mFailedResultsCounter = 0;\r
-        mLastFailedResult = null;\r
-        mConflictsFound = 0;\r
-        mFailsInFavouritesFound = 0;\r
-        mForgottenLocalFiles = new HashMap<String, String>();\r
-        mSyncResult = syncResult;\r
-        mSyncResult.fullSyncRequested = false;\r
-        mSyncResult.delayUntil = 60*60*24; // sync after 24h\r
-\r
-        this.setAccount(account);\r
-        this.setContentProvider(provider);\r
-        this.setStorageManager(new FileDataStorageManager(account, getContentProvider()));\r
-        try {\r
-            this.initClientForCurrentAccount();\r
-        } catch (IOException e) {\r
-            /// the account is unknown for the Synchronization Manager, or unreachable for this context; don't try this again\r
-            mSyncResult.tooManyRetries = true;\r
-            notifyFailedSynchronization();\r
-            return;\r
-        } catch (AccountsException e) {\r
-            /// the account is unknown for the Synchronization Manager, or unreachable for this context; don't try this again\r
-            mSyncResult.tooManyRetries = true;\r
-            notifyFailedSynchronization();\r
-            return;\r
-        }\r
-        \r
-        Log.d(TAG, "Synchronization of ownCloud account " + account.name + " starting");\r
-        sendStickyBroadcast(true, null, null);  // message to signal the start of the synchronization to the UI\r
-        \r
-        try {\r
-            updateOCVersion();\r
-            mCurrentSyncTime = System.currentTimeMillis();\r
-            if (!mCancellation) {\r
-                fetchData(OCFile.PATH_SEPARATOR, DataStorageManager.ROOT_PARENT_ID);\r
-                \r
-            } else {\r
-                Log.d(TAG, "Leaving synchronization before any remote request due to cancellation was requested");\r
-            }\r
-            \r
-            \r
-        } finally {\r
-            // it's important making this although very unexpected errors occur; that's the reason for the finally\r
-            \r
-            if (mFailedResultsCounter > 0 && mIsManualSync) {\r
-                /// don't let the system synchronization manager retries MANUAL synchronizations\r
-                //      (be careful: "MANUAL" currently includes the synchronization requested when a new account is created and when the user changes the current account)\r
-                mSyncResult.tooManyRetries = true;\r
-                \r
-                /// notify the user about the failure of MANUAL synchronization\r
-                notifyFailedSynchronization();\r
-                \r
-            }\r
-            if (mConflictsFound > 0 || mFailsInFavouritesFound > 0) {\r
-                notifyFailsInFavourites();\r
-            }\r
-            if (mForgottenLocalFiles.size() > 0) {\r
-                notifyForgottenLocalFiles();\r
-                \r
-            }\r
-            sendStickyBroadcast(false, null, mLastFailedResult);        // message to signal the end to the UI\r
-        }\r
-        \r
-    }\r
-\r
-    \r
-    /**\r
-     * Called by system SyncManager when a synchronization is required to be cancelled.\r
-     * \r
-     * Sets the mCancellation flag to 'true'. THe synchronization will be stopped when before a new folder is fetched. Data of the last folder\r
-     * fetched will be still saved in the database. See onPerformSync implementation.\r
-     */\r
-    @Override\r
-    public void onSyncCanceled() {\r
-        Log.d(TAG, "Synchronization of " + getAccount().name + " has been requested to cancel");\r
-        mCancellation = true;\r
-        super.onSyncCanceled();\r
-    }\r
-    \r
-    \r
-    /**\r
-     * Updates the locally stored version value of the ownCloud server\r
-     */\r
-    private void updateOCVersion() {\r
-        UpdateOCVersionOperation update = new UpdateOCVersionOperation(getAccount(), getContext());\r
-        RemoteOperationResult result = update.execute(getClient());\r
-        if (!result.isSuccess()) {\r
-            mLastFailedResult = result; \r
-        }\r
-    }\r
-\r
-    \r
-    \r
-    /**\r
-     * Synchronize the properties of files and folders contained in a remote folder given by remotePath.\r
-     * \r
-     * @param remotePath        Remote path to the folder to synchronize.\r
-     * @param parentId          Database Id of the folder to synchronize.\r
-     */\r
-    private void fetchData(String remotePath, long parentId) {\r
-        \r
-        if (mFailedResultsCounter > MAX_FAILED_RESULTS || isFinisher(mLastFailedResult))\r
-            return;\r
-        \r
-        // perform folder synchronization\r
-        SynchronizeFolderOperation synchFolderOp = new SynchronizeFolderOperation(  remotePath, \r
-                                                                                    mCurrentSyncTime, \r
-                                                                                    parentId, \r
-                                                                                    getStorageManager(), \r
-                                                                                    getAccount(), \r
-                                                                                    getContext()\r
-                                                                                  );\r
-        RemoteOperationResult result = synchFolderOp.execute(getClient());\r
-        \r
-        \r
-        // synchronized folder -> notice to UI - ALWAYS, although !result.isSuccess\r
-        sendStickyBroadcast(true, remotePath, null);\r
-        \r
-        if (result.isSuccess() || result.getCode() == ResultCode.SYNC_CONFLICT) {\r
-            \r
-            if (result.getCode() == ResultCode.SYNC_CONFLICT) {\r
-                mConflictsFound += synchFolderOp.getConflictsFound();\r
-                mFailsInFavouritesFound += synchFolderOp.getFailsInFavouritesFound();\r
-            }\r
-            if (synchFolderOp.getForgottenLocalFiles().size() > 0) {\r
-                mForgottenLocalFiles.putAll(synchFolderOp.getForgottenLocalFiles());\r
-            }\r
-            // synchronize children folders \r
-            List<OCFile> children = synchFolderOp.getChildren();\r
-            fetchChildren(children);    // beware of the 'hidden' recursion here!\r
-            \r
-        } else {\r
-            if (result.getCode() == RemoteOperationResult.ResultCode.UNAUTHORIZED) {\r
-                mSyncResult.stats.numAuthExceptions++;\r
-                \r
-            } else if (result.getException() instanceof DavException) {\r
-                mSyncResult.stats.numParseExceptions++;\r
-                \r
-            } else if (result.getException() instanceof IOException) { \r
-                mSyncResult.stats.numIoExceptions++;\r
-            }\r
-            mFailedResultsCounter++;\r
-            mLastFailedResult = result;\r
-        }\r
-            \r
-    }\r
-\r
-    /**\r
-     * Checks if a failed result should terminate the synchronization process immediately, according to\r
-     * OUR OWN POLICY\r
-     * \r
-     * @param   failedResult        Remote operation result to check.\r
-     * @return                      'True' if the result should immediately finish the synchronization\r
-     */\r
-    private boolean isFinisher(RemoteOperationResult failedResult) {\r
-        if  (failedResult != null) {\r
-            RemoteOperationResult.ResultCode code = failedResult.getCode();\r
-            return (code.equals(RemoteOperationResult.ResultCode.SSL_ERROR) ||\r
-                    code.equals(RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) ||\r
-                    code.equals(RemoteOperationResult.ResultCode.UNAUTHORIZED) ||\r
-                    code.equals(RemoteOperationResult.ResultCode.BAD_OC_VERSION) ||\r
-                    code.equals(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED));\r
-        }\r
-        return false;\r
-    }\r
-\r
-    /**\r
-     * Synchronize data of folders in the list of received files\r
-     * \r
-     * @param files         Files to recursively fetch \r
-     */\r
-    private void fetchChildren(List<OCFile> files) {\r
-        int i;\r
-        for (i=0; i < files.size() && !mCancellation; i++) {\r
-            OCFile newFile = files.get(i);\r
-            if (newFile.isDirectory()) {\r
-                fetchData(newFile.getRemotePath(), newFile.getFileId());\r
-            }\r
-        }\r
-        if (mCancellation && i <files.size()) Log.d(TAG, "Leaving synchronization before synchronizing " + files.get(i).getRemotePath() + " because cancelation request");\r
-    }\r
-\r
-    \r
-    /**\r
-     * Sends a message to any application component interested in the progress of the synchronization.\r
-     * \r
-     * @param inProgress        'True' when the synchronization progress is not finished.\r
-     * @param dirRemotePath     Remote path of a folder that was just synchronized (with or without success)\r
-     */\r
-    private void sendStickyBroadcast(boolean inProgress, String dirRemotePath, RemoteOperationResult result) {\r
-        Intent i = new Intent(FileSyncService.SYNC_MESSAGE);\r
-        i.putExtra(FileSyncService.IN_PROGRESS, inProgress);\r
-        i.putExtra(FileSyncService.ACCOUNT_NAME, getAccount().name);\r
-        if (dirRemotePath != null) {\r
-            i.putExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH, dirRemotePath);\r
-        }\r
-        if (result != null) {\r
-            i.putExtra(FileSyncService.SYNC_RESULT, result);\r
-        }\r
-        getContext().sendStickyBroadcast(i);\r
-    }\r
-\r
-    \r
-    \r
-    /**\r
-     * Notifies the user about a failed synchronization through the status notification bar \r
-     */\r
-    private void notifyFailedSynchronization() {\r
-        Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_fail_ticker), System.currentTimeMillis());\r
-        notification.flags |= Notification.FLAG_AUTO_CANCEL;\r
-        boolean needsToUpdateCredentials = (mLastFailedResult != null && mLastFailedResult.getCode() == ResultCode.UNAUTHORIZED);\r
-        // TODO put something smart in the contentIntent below for all the possible errors\r
-        notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);\r
-        if (needsToUpdateCredentials) {\r
-            // let the user update credentials with one click\r
-            Intent updateAccountCredentials = new Intent(getContext(), AuthenticatorActivity.class);\r
-            updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, getAccount());\r
-            updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_TOKEN);\r
-            updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\r
-            updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);\r
-            updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND);\r
-            notification.contentIntent = PendingIntent.getActivity(getContext(), (int)System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT);\r
-            notification.setLatestEventInfo(getContext().getApplicationContext(), \r
-                    getContext().getString(R.string.sync_fail_ticker), \r
-                    String.format(getContext().getString(R.string.sync_fail_content_unauthorized), getAccount().name), \r
-                    notification.contentIntent);\r
-        } else {\r
-            notification.setLatestEventInfo(getContext().getApplicationContext(), \r
-                    getContext().getString(R.string.sync_fail_ticker), \r
-                    String.format(getContext().getString(R.string.sync_fail_content), getAccount().name), \r
-                    notification.contentIntent);\r
-        }\r
-        ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_fail_ticker, notification);\r
-    }\r
-\r
-\r
-    /**\r
-     * Notifies the user about conflicts and strange fails when trying to synchronize the contents of kept-in-sync files.\r
-     * \r
-     * By now, we won't consider a failed synchronization.\r
-     */\r
-    private void notifyFailsInFavourites() {\r
-        if (mFailedResultsCounter > 0) {\r
-            Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_fail_in_favourites_ticker), System.currentTimeMillis());\r
-            notification.flags |= Notification.FLAG_AUTO_CANCEL;\r
-            // TODO put something smart in the contentIntent below\r
-            notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);\r
-            notification.setLatestEventInfo(getContext().getApplicationContext(), \r
-                                            getContext().getString(R.string.sync_fail_in_favourites_ticker), \r
-                                            String.format(getContext().getString(R.string.sync_fail_in_favourites_content), mFailedResultsCounter + mConflictsFound, mConflictsFound), \r
-                                            notification.contentIntent);\r
-            ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_fail_in_favourites_ticker, notification);\r
-            \r
-        } else {\r
-            Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_conflicts_in_favourites_ticker), System.currentTimeMillis());\r
-            notification.flags |= Notification.FLAG_AUTO_CANCEL;\r
-            // TODO put something smart in the contentIntent below\r
-            notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);\r
-            notification.setLatestEventInfo(getContext().getApplicationContext(), \r
-                                            getContext().getString(R.string.sync_conflicts_in_favourites_ticker), \r
-                                            String.format(getContext().getString(R.string.sync_conflicts_in_favourites_content), mConflictsFound), \r
-                                            notification.contentIntent);\r
-            ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_conflicts_in_favourites_ticker, notification);\r
-        } \r
-    }\r
-\r
-    \r
-    /**\r
-     * Notifies the user about local copies of files out of the ownCloud local directory that were 'forgotten' because \r
-     * copying them inside the ownCloud local directory was not possible.\r
-     * \r
-     * We don't want links to files out of the ownCloud local directory (foreign files) anymore. It's easy to have \r
-     * synchronization problems if a local file is linked to more than one remote file.\r
-     * \r
-     * We won't consider a synchronization as failed when foreign files can not be copied to the ownCloud local directory.\r
-     */\r
-    private void notifyForgottenLocalFiles() {\r
-        Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_foreign_files_forgotten_ticker), System.currentTimeMillis());\r
-        notification.flags |= Notification.FLAG_AUTO_CANCEL;\r
-\r
-        /// includes a pending intent in the notification showing a more detailed explanation\r
-        Intent explanationIntent = new Intent(getContext(), ErrorsWhileCopyingHandlerActivity.class);\r
-        explanationIntent.putExtra(ErrorsWhileCopyingHandlerActivity.EXTRA_ACCOUNT, getAccount());\r
-        ArrayList<String> remotePaths = new ArrayList<String>();\r
-        ArrayList<String> localPaths = new ArrayList<String>();\r
-        remotePaths.addAll(mForgottenLocalFiles.keySet());\r
-        localPaths.addAll(mForgottenLocalFiles.values());\r
-        explanationIntent.putExtra(ErrorsWhileCopyingHandlerActivity.EXTRA_LOCAL_PATHS, localPaths);\r
-        explanationIntent.putExtra(ErrorsWhileCopyingHandlerActivity.EXTRA_REMOTE_PATHS, remotePaths);  \r
-        explanationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);\r
-        \r
-        notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), explanationIntent, 0);\r
-        notification.setLatestEventInfo(getContext().getApplicationContext(), \r
-                                        getContext().getString(R.string.sync_foreign_files_forgotten_ticker), \r
-                                        String.format(getContext().getString(R.string.sync_foreign_files_forgotten_content), mForgottenLocalFiles.size(), getContext().getString(R.string.app_name)), \r
-                                        notification.contentIntent);\r
-        ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_foreign_files_forgotten_ticker, notification);\r
-        \r
-    }\r
-    \r
-    \r
-}\r
+/* ownCloud Android client application
+ *   Copyright (C) 2011  Bartek Przybylski
+ *   Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License 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.syncadapter;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.jackrabbit.webdav.DavException;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AuthenticatorActivity;
+import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.operations.RemoteOperationResult;
+import com.owncloud.android.operations.SynchronizeFolderOperation;
+import com.owncloud.android.operations.UpdateOCVersionOperation;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.ui.activity.ErrorsWhileCopyingHandlerActivity;
+import android.accounts.Account;
+import android.accounts.AccountsException;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SyncResult;
+import android.os.Bundle;
+
+/**
+ * SyncAdapter implementation for syncing sample SyncAdapter contacts to the
+ * platform ContactOperations provider.
+ * 
+ * @author Bartek Przybylski
+ * @author David A. Velasco
+ */
+public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
+
+    private final static String TAG = "FileSyncAdapter";
+
+    /** 
+     * Maximum number of failed folder synchronizations that are supported before finishing the synchronization operation
+     */
+    private static final int MAX_FAILED_RESULTS = 3; 
+    
+    private long mCurrentSyncTime;
+    private boolean mCancellation;
+    private boolean mIsManualSync;
+    private int mFailedResultsCounter;    
+    private RemoteOperationResult mLastFailedResult;
+    private SyncResult mSyncResult;
+    private int mConflictsFound;
+    private int mFailsInFavouritesFound;
+    private Map<String, String> mForgottenLocalFiles;
+
+    
+    public FileSyncAdapter(Context context, boolean autoInitialize) {
+        super(context, autoInitialize);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public synchronized void onPerformSync(Account account, Bundle extras,
+            String authority, ContentProviderClient provider,
+            SyncResult syncResult) {
+
+        mCancellation = false;
+        mIsManualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
+        mFailedResultsCounter = 0;
+        mLastFailedResult = null;
+        mConflictsFound = 0;
+        mFailsInFavouritesFound = 0;
+        mForgottenLocalFiles = new HashMap<String, String>();
+        mSyncResult = syncResult;
+        mSyncResult.fullSyncRequested = false;
+        mSyncResult.delayUntil = 60*60*24; // sync after 24h
+
+        this.setAccount(account);
+        this.setContentProvider(provider);
+        this.setStorageManager(new FileDataStorageManager(account, getContentProvider()));
+        try {
+            this.initClientForCurrentAccount();
+        } catch (IOException e) {
+            /// the account is unknown for the Synchronization Manager, or unreachable for this context; don't try this again
+            mSyncResult.tooManyRetries = true;
+            notifyFailedSynchronization();
+            return;
+        } catch (AccountsException e) {
+            /// the account is unknown for the Synchronization Manager, or unreachable for this context; don't try this again
+            mSyncResult.tooManyRetries = true;
+            notifyFailedSynchronization();
+            return;
+        }
+        
+        Log_OC.d(TAG, "Synchronization of ownCloud account " + account.name + " starting");
+        sendStickyBroadcast(true, null, null);  // message to signal the start of the synchronization to the UI
+        
+        try {
+            updateOCVersion();
+            mCurrentSyncTime = System.currentTimeMillis();
+            if (!mCancellation) {
+                fetchData(OCFile.PATH_SEPARATOR, DataStorageManager.ROOT_PARENT_ID);
+                
+            } else {
+                Log_OC.d(TAG, "Leaving synchronization before any remote request due to cancellation was requested");
+            }
+            
+            
+        } finally {
+            // it's important making this although very unexpected errors occur; that's the reason for the finally
+            
+            if (mFailedResultsCounter > 0 && mIsManualSync) {
+                /// don't let the system synchronization manager retries MANUAL synchronizations
+                //      (be careful: "MANUAL" currently includes the synchronization requested when a new account is created and when the user changes the current account)
+                mSyncResult.tooManyRetries = true;
+                
+                /// notify the user about the failure of MANUAL synchronization
+                notifyFailedSynchronization();
+                
+            }
+            if (mConflictsFound > 0 || mFailsInFavouritesFound > 0) {
+                notifyFailsInFavourites();
+            }
+            if (mForgottenLocalFiles.size() > 0) {
+                notifyForgottenLocalFiles();
+                
+            }
+            sendStickyBroadcast(false, null, mLastFailedResult);        // message to signal the end to the UI
+        }
+        
+    }
+    
+    /**
+     * Called by system SyncManager when a synchronization is required to be cancelled.
+     * 
+     * Sets the mCancellation flag to 'true'. THe synchronization will be stopped when before a new folder is fetched. Data of the last folder
+     * fetched will be still saved in the database. See onPerformSync implementation.
+     */
+    @Override
+    public void onSyncCanceled() {
+        Log_OC.d(TAG, "Synchronization of " + getAccount().name + " has been requested to cancel");
+        mCancellation = true;
+        super.onSyncCanceled();
+    }
+    
+    
+    /**
+     * Updates the locally stored version value of the ownCloud server
+     */
+    private void updateOCVersion() {
+        UpdateOCVersionOperation update = new UpdateOCVersionOperation(getAccount(), getContext());
+        RemoteOperationResult result = update.execute(getClient());
+        if (!result.isSuccess()) {
+            mLastFailedResult = result; 
+        }
+    }
+    
+    
+    /**
+     * Synchronize the properties of files and folders contained in a remote folder given by remotePath.
+     * 
+     * @param remotePath        Remote path to the folder to synchronize.
+     * @param parentId          Database Id of the folder to synchronize.
+     */
+    private void fetchData(String remotePath, long parentId) {
+        
+        if (mFailedResultsCounter > MAX_FAILED_RESULTS || isFinisher(mLastFailedResult))
+            return;
+        
+        // perform folder synchronization
+        SynchronizeFolderOperation synchFolderOp = new SynchronizeFolderOperation(  remotePath, 
+                                                                                    mCurrentSyncTime, 
+                                                                                    parentId, 
+                                                                                    getStorageManager(), 
+                                                                                    getAccount(), 
+                                                                                    getContext()
+                                                                                  );
+        RemoteOperationResult result = synchFolderOp.execute(getClient());
+        
+        
+        // synchronized folder -> notice to UI - ALWAYS, although !result.isSuccess
+        sendStickyBroadcast(true, remotePath, null);
+        
+        if (result.isSuccess() || result.getCode() == ResultCode.SYNC_CONFLICT) {
+            
+            if (result.getCode() == ResultCode.SYNC_CONFLICT) {
+                mConflictsFound += synchFolderOp.getConflictsFound();
+                mFailsInFavouritesFound += synchFolderOp.getFailsInFavouritesFound();
+            }
+            if (synchFolderOp.getForgottenLocalFiles().size() > 0) {
+                mForgottenLocalFiles.putAll(synchFolderOp.getForgottenLocalFiles());
+            }
+            // synchronize children folders 
+            List<OCFile> children = synchFolderOp.getChildren();
+            fetchChildren(children);    // beware of the 'hidden' recursion here!
+            
+        } else {
+            if (result.getCode() == RemoteOperationResult.ResultCode.UNAUTHORIZED) {
+                mSyncResult.stats.numAuthExceptions++;
+                
+            } else if (result.getException() instanceof DavException) {
+                mSyncResult.stats.numParseExceptions++;
+                
+            } else if (result.getException() instanceof IOException) { 
+                mSyncResult.stats.numIoExceptions++;
+            }
+            mFailedResultsCounter++;
+            mLastFailedResult = result;
+        }
+            
+    }
+
+    /**
+     * Checks if a failed result should terminate the synchronization process immediately, according to
+     * OUR OWN POLICY
+     * 
+     * @param   failedResult        Remote operation result to check.
+     * @return                      'True' if the result should immediately finish the synchronization
+     */
+    private boolean isFinisher(RemoteOperationResult failedResult) {
+        if  (failedResult != null) {
+            RemoteOperationResult.ResultCode code = failedResult.getCode();
+            return (code.equals(RemoteOperationResult.ResultCode.SSL_ERROR) ||
+                    code.equals(RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) ||
+                    code.equals(RemoteOperationResult.ResultCode.BAD_OC_VERSION) ||
+                    code.equals(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED));
+        }
+        return false;
+    }
+
+    /**
+     * Synchronize data of folders in the list of received files
+     * 
+     * @param files         Files to recursively fetch 
+     */
+    private void fetchChildren(List<OCFile> files) {
+        int i;
+        for (i=0; i < files.size() && !mCancellation; i++) {
+            OCFile newFile = files.get(i);
+            if (newFile.isDirectory()) {
+                fetchData(newFile.getRemotePath(), newFile.getFileId());
+            }
+        }
+        if (mCancellation && i <files.size()) Log_OC.d(TAG, "Leaving synchronization before synchronizing " + files.get(i).getRemotePath() + " because cancelation request");
+    }
+
+    
+    /**
+     * Sends a message to any application component interested in the progress of the synchronization.
+     * 
+     * @param inProgress        'True' when the synchronization progress is not finished.
+     * @param dirRemotePath     Remote path of a folder that was just synchronized (with or without success)
+     */
+    private void sendStickyBroadcast(boolean inProgress, String dirRemotePath, RemoteOperationResult result) {
+        Intent i = new Intent(FileSyncService.SYNC_MESSAGE);
+        i.putExtra(FileSyncService.IN_PROGRESS, inProgress);
+        i.putExtra(FileSyncService.ACCOUNT_NAME, getAccount().name);
+        if (dirRemotePath != null) {
+            i.putExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH, dirRemotePath);
+        }
+        if (result != null) {
+            i.putExtra(FileSyncService.SYNC_RESULT, result);
+        }
+        getContext().sendStickyBroadcast(i);
+    }
+
+    
+    
+    /**
+     * Notifies the user about a failed synchronization through the status notification bar 
+     */
+    private void notifyFailedSynchronization() {
+        Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_fail_ticker), System.currentTimeMillis());
+        notification.flags |= Notification.FLAG_AUTO_CANCEL;
+        boolean needsToUpdateCredentials = (mLastFailedResult != null && mLastFailedResult.getCode() == ResultCode.UNAUTHORIZED);
+        // TODO put something smart in the contentIntent below for all the possible errors
+        notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);
+        if (needsToUpdateCredentials) {
+            // let the user update credentials with one click
+            Intent updateAccountCredentials = new Intent(getContext(), AuthenticatorActivity.class);
+            updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, getAccount());
+            updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_TOKEN);
+            updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+            updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND);
+            notification.contentIntent = PendingIntent.getActivity(getContext(), (int)System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT);
+            notification.setLatestEventInfo(getContext().getApplicationContext(), 
+                    getContext().getString(R.string.sync_fail_ticker), 
+                    String.format(getContext().getString(R.string.sync_fail_content_unauthorized), getAccount().name), 
+                    notification.contentIntent);
+        } else {
+            notification.setLatestEventInfo(getContext().getApplicationContext(), 
+                                            getContext().getString(R.string.sync_fail_ticker), 
+                                            String.format(getContext().getString(R.string.sync_fail_content), getAccount().name), 
+                                            notification.contentIntent);
+        }
+        ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_fail_ticker, notification);
+    }
+
+
+    /**
+     * Notifies the user about conflicts and strange fails when trying to synchronize the contents of kept-in-sync files.
+     * 
+     * By now, we won't consider a failed synchronization.
+     */
+    private void notifyFailsInFavourites() {
+        if (mFailedResultsCounter > 0) {
+            Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_fail_in_favourites_ticker), System.currentTimeMillis());
+            notification.flags |= Notification.FLAG_AUTO_CANCEL;
+            // TODO put something smart in the contentIntent below
+            notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);
+            notification.setLatestEventInfo(getContext().getApplicationContext(), 
+                                            getContext().getString(R.string.sync_fail_in_favourites_ticker), 
+                                            String.format(getContext().getString(R.string.sync_fail_in_favourites_content), mFailedResultsCounter + mConflictsFound, mConflictsFound), 
+                                            notification.contentIntent);
+            ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_fail_in_favourites_ticker, notification);
+            
+        } else {
+            Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_conflicts_in_favourites_ticker), System.currentTimeMillis());
+            notification.flags |= Notification.FLAG_AUTO_CANCEL;
+            // TODO put something smart in the contentIntent below
+            notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);
+            notification.setLatestEventInfo(getContext().getApplicationContext(), 
+                                            getContext().getString(R.string.sync_conflicts_in_favourites_ticker), 
+                                            String.format(getContext().getString(R.string.sync_conflicts_in_favourites_content), mConflictsFound), 
+                                            notification.contentIntent);
+            ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_conflicts_in_favourites_ticker, notification);
+        } 
+    }
+    
+    /**
+     * Notifies the user about local copies of files out of the ownCloud local directory that were 'forgotten' because 
+     * copying them inside the ownCloud local directory was not possible.
+     * 
+     * We don't want links to files out of the ownCloud local directory (foreign files) anymore. It's easy to have 
+     * synchronization problems if a local file is linked to more than one remote file.
+     * 
+     * We won't consider a synchronization as failed when foreign files can not be copied to the ownCloud local directory.
+     */
+    private void notifyForgottenLocalFiles() {
+        Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_foreign_files_forgotten_ticker), System.currentTimeMillis());
+        notification.flags |= Notification.FLAG_AUTO_CANCEL;
+
+        /// includes a pending intent in the notification showing a more detailed explanation
+        Intent explanationIntent = new Intent(getContext(), ErrorsWhileCopyingHandlerActivity.class);
+        explanationIntent.putExtra(ErrorsWhileCopyingHandlerActivity.EXTRA_ACCOUNT, getAccount());
+        ArrayList<String> remotePaths = new ArrayList<String>();
+        ArrayList<String> localPaths = new ArrayList<String>();
+        remotePaths.addAll(mForgottenLocalFiles.keySet());
+        localPaths.addAll(mForgottenLocalFiles.values());
+        explanationIntent.putExtra(ErrorsWhileCopyingHandlerActivity.EXTRA_LOCAL_PATHS, localPaths);
+        explanationIntent.putExtra(ErrorsWhileCopyingHandlerActivity.EXTRA_REMOTE_PATHS, remotePaths);  
+        explanationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        
+        notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), explanationIntent, 0);
+        notification.setLatestEventInfo(getContext().getApplicationContext(), 
+                                        getContext().getString(R.string.sync_foreign_files_forgotten_ticker), 
+                                        String.format(getContext().getString(R.string.sync_foreign_files_forgotten_content), mForgottenLocalFiles.size(), getContext().getString(R.string.app_name)), 
+                                        notification.contentIntent);
+        ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_foreign_files_forgotten_ticker, notification);
+        
+    }
+    
+    
+}
index 3d81961..f6c70dc 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.\r
  *\r
  *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
+ *   it under the terms of the GNU General Public License version 2,\r
+ *   as published by the Free Software Foundation.\r
  *\r
  *   This program is distributed in the hope that it will be useful,\r
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index e97dfef..a65f3ad 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.\r
  *\r
  *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
+ *   it under the terms of the GNU General Public License version 2,\r
+ *   as published by the Free Software Foundation.\r
  *\r
  *   This program is distributed in the hope that it will be useful,\r
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index a9315dd..fccf56d 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.\r
  *\r
  *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
+ *   it under the terms of the GNU General Public License version 2,\r
+ *   as published by the Free Software Foundation.\r
  *\r
  *   This program is distributed in the hope that it will be useful,\r
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index 5b7fb98..9fe885b 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index d791eed..43b2fc6 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index a6d021d..8ae71ec 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.\r
  *\r
  *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
+ *   it under the terms of the GNU General Public License version 2,\r
+ *   as published by the Free Software Foundation.\r
  *\r
  *   This program is distributed in the hope that it will be useful,\r
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index 2c29e62..be369dc 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -33,7 +32,6 @@ import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.Handler;
-import android.util.Log;
 import android.view.ContextMenu;
 import android.view.View;
 import android.view.ViewGroup;
@@ -51,6 +49,7 @@ import com.actionbarsherlock.view.MenuInflater;
 import com.actionbarsherlock.view.MenuItem;
 import com.owncloud.android.AccountUtils;
 import com.owncloud.android.authentication.AccountAuthenticator;
+import com.owncloud.android.Log_OC;
 
 import com.owncloud.android.R;
 
@@ -153,7 +152,7 @@ public class AccountSelectActivity extends SherlockListActivity implements
         try {
             map = (HashMap<String, String>) getListAdapter().getItem(index);
         } catch (ClassCastException e) {
-            Log.wtf(TAG, "getitem(index) from list adapter did not return hashmap, bailing out");
+            Log_OC.wtf(TAG, "getitem(index) from list adapter did not return hashmap, bailing out");
             return false;
         }
         
index 21aaed8..62c5f46 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -20,6 +19,7 @@
 package com.owncloud.android.ui.activity;
 
 import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.services.FileUploader;
 import com.owncloud.android.ui.dialog.ConflictsResolveDialog;
@@ -29,7 +29,6 @@ import com.owncloud.android.ui.dialog.ConflictsResolveDialog.OnConflictDecisionM
 import android.accounts.Account;
 import android.content.Intent;
 import android.os.Bundle;
-import android.util.Log;
 
 /**
  * Wrapper activity which will be launched if keep-in-sync file will be modified by external
@@ -79,7 +78,7 @@ public class ConflictsResolveActivity extends SherlockFragmentActivity implement
                 i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);
                 break;
             default:
-                Log.wtf(TAG, "Unhandled conflict decision " + decision);
+                Log_OC.wtf(TAG, "Unhandled conflict decision " + decision);
                 return;
         }
         i.putExtra(FileUploader.KEY_ACCOUNT, mOCAccount);
index 0522cb8..d61626c 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -29,7 +28,6 @@ import android.os.Bundle;
 import android.os.Handler;
 import android.support.v4.app.DialogFragment;
 import android.text.method.ScrollingMovementMethod;
-import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -41,6 +39,7 @@ import android.widget.TextView;
 import android.widget.Toast;
 
 import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.R;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
@@ -52,7 +51,7 @@ import com.owncloud.android.utils.FileStorageUtils;
  * Activity reporting errors occurred when local files uploaded to an ownCloud account with an app in
  * version under 1.3.16 where being copied to the ownCloud local folder.
  * 
- * Allows the user move the files to the ownCloud local folder, or let them unlinked to the remote
+ * Allows the user move the files to the ownCloud local folder. let them unlinked to the remote
  * files.
  * 
  * Shown when the error notification summarizing the list of errors is clicked by the user.
@@ -182,16 +181,16 @@ public class ErrorsWhileCopyingHandlerActivity  extends SherlockFragmentActivity
     public void onClick(View v) {
         if (v.getId() == R.id.ok) {
             /// perform movement operation in background thread
-            Log.d(TAG, "Clicked MOVE, start movement");
+            Log_OC.d(TAG, "Clicked MOVE, start movement");
             new MoveFilesTask().execute();            
             
         } else if (v.getId() == R.id.cancel) {
             /// just finish
-            Log.d(TAG, "Clicked CANCEL, bye");
+            Log_OC.d(TAG, "Clicked CANCEL, bye");
             finish();
             
         } else {
-            Log.e(TAG, "Clicked phantom button, id: " + v.getId());
+            Log_OC.e(TAG, "Clicked phantom button, id: " + v.getId());
         }
     }
 
diff --git a/src/com/owncloud/android/ui/activity/FailedUploadActivity.java b/src/com/owncloud/android/ui/activity/FailedUploadActivity.java
new file mode 100644 (file)
index 0000000..4c13fb2
--- /dev/null
@@ -0,0 +1,55 @@
+/* ownCloud Android client application\r
+ *   Copyright (C) 2012-2013 ownCloud Inc.\r
+ *\r
+ *   This program is free software: you can redistribute it and/or modify\r
+ *   it under the terms of the GNU General Public License version 2,\r
+ *   as published by the Free Software Foundation.\r
+ *\r
+ *   This program is distributed in the hope that it will be useful,\r
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ *   GNU General Public License for more details.\r
+ *\r
+ *   You should have received a copy of the GNU General Public License\r
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
+ *\r
+ */\r
+\r
+package com.owncloud.android.ui.activity;\r
+\r
+import android.app.Activity;\r
+import android.os.Bundle;\r
+import android.view.View;\r
+import android.view.View.OnClickListener;\r
+import android.widget.Button;\r
+import android.widget.TextView;\r
+\r
+import com.owncloud.android.R;\r
+\r
+/**\r
+ * This Activity is used to display a detail message for failed uploads\r
+ * \r
+ * The entry-point for this activity is the 'Failed upload Notification"\r
+ * \r
+ * @author andomaex / Matthias Baumann\r
+ */\r
+public class FailedUploadActivity extends Activity {\r
+\r
+    public static final String MESSAGE = "message";\r
+\r
+    @Override\r
+    public void onCreate(Bundle savedInstanceState) {\r
+        super.onCreate(savedInstanceState);\r
+        setContentView(R.layout.failed_upload_message_view);\r
+        String message = getIntent().getStringExtra(MESSAGE);\r
+        TextView textView = (TextView) findViewById(R.id.faild_upload_message);\r
+        textView.setText(message);\r
+        Button close_button = (Button) findViewById(R.id.failed_uploadactivity_close_button);\r
+        close_button.setOnClickListener(new OnClickListener() {\r
+            @Override\r
+            public void onClick(View v) {\r
+                finish();\r
+            }\r
+        });\r
+    }\r
+}\r
index cd68c89..4cd9363 100644 (file)
-/* ownCloud Android client application\r
- *   Copyright (C) 2011  Bartek Przybylski\r
- *   Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
- *\r
- *   This program is distributed in the hope that it will be useful,\r
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- *   GNU General Public License for more details.\r
- *\r
- *   You should have received a copy of the GNU General Public License\r
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-package com.owncloud.android.ui.activity;\r
-\r
-import android.accounts.Account;\r
-import android.app.Dialog;\r
-import android.app.ProgressDialog;\r
-import android.content.ComponentName;\r
-import android.content.Context;\r
-import android.content.Intent;\r
-import android.content.ServiceConnection;\r
-import android.content.res.Configuration;\r
-import android.os.Bundle;\r
-import android.os.IBinder;\r
-import android.support.v4.app.FragmentTransaction;\r
-import android.util.Log;\r
-\r
-import com.actionbarsherlock.app.ActionBar;\r
-import com.actionbarsherlock.app.SherlockFragmentActivity;\r
-import com.actionbarsherlock.view.MenuItem;\r
-import com.owncloud.android.datamodel.OCFile;\r
-import com.owncloud.android.files.services.FileDownloader;\r
-import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;\r
-import com.owncloud.android.files.services.FileUploader;\r
-import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;\r
-import com.owncloud.android.ui.fragment.FileDetailFragment;\r
-\r
-import com.owncloud.android.R;\r
-\r
-/**\r
- * This activity displays the details of a file like its name, its size and so\r
- * on.\r
- * \r
- * @author Bartek Przybylski\r
- * \r
- */\r
-public class FileDetailActivity extends SherlockFragmentActivity implements FileDetailFragment.ContainerActivity {\r
-    \r
-    public static final int DIALOG_SHORT_WAIT = 0;\r
-\r
-    public static final String TAG = FileDetailActivity.class.getSimpleName();\r
-    \r
-    private boolean mConfigurationChangedToLandscape = false;\r
-    private FileDownloaderBinder mDownloaderBinder = null;\r
-    private ServiceConnection mDownloadConnection, mUploadConnection = null;\r
-    private FileUploaderBinder mUploaderBinder = null;\r
-\r
-\r
-    @Override\r
-    protected void onCreate(Bundle savedInstanceState) {\r
-        super.onCreate(savedInstanceState);\r
-\r
-        // check if configuration changed to large-land ; for a tablet being changed from portrait to landscape when in FileDetailActivity \r
-        Configuration conf = getResources().getConfiguration();\r
-        mConfigurationChangedToLandscape = (conf.orientation == Configuration.ORIENTATION_LANDSCAPE && \r
-                                                (conf.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE\r
-                                           );\r
-\r
-        if (!mConfigurationChangedToLandscape) {\r
-            mDownloadConnection = new DetailsServiceConnection();\r
-            bindService(new Intent(this, FileDownloader.class), mDownloadConnection, Context.BIND_AUTO_CREATE);\r
-            mUploadConnection = new DetailsServiceConnection();\r
-            bindService(new Intent(this, FileUploader.class), mUploadConnection, Context.BIND_AUTO_CREATE);\r
-            \r
-            setContentView(R.layout.file_activity_details);\r
-        \r
-            ActionBar actionBar = getSupportActionBar();\r
-            actionBar.setDisplayHomeAsUpEnabled(true);\r
-\r
-            OCFile file = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE);\r
-            Account account = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_ACCOUNT);\r
-            FileDetailFragment mFileDetail = new FileDetailFragment(file, account);\r
-        \r
-            FragmentTransaction ft = getSupportFragmentManager().beginTransaction();\r
-            ft.replace(R.id.fragment, mFileDetail, FileDetailFragment.FTAG);\r
-            ft.commit();\r
-            \r
-        }  else {\r
-            backToDisplayActivity();   // the 'back' won't be effective until this.onStart() and this.onResume() are completed;\r
-        }\r
-        \r
-        \r
-    }\r
-    \r
-    \r
-    /** Defines callbacks for service binding, passed to bindService() */\r
-    private class DetailsServiceConnection implements ServiceConnection {\r
-\r
-        @Override\r
-        public void onServiceConnected(ComponentName component, IBinder service) {\r
-            if (component.equals(new ComponentName(FileDetailActivity.this, FileDownloader.class))) {\r
-                Log.d(TAG, "Download service connected");\r
-                mDownloaderBinder = (FileDownloaderBinder) service;\r
-            } else if (component.equals(new ComponentName(FileDetailActivity.this, FileUploader.class))) {\r
-                Log.d(TAG, "Upload service connected");\r
-                mUploaderBinder = (FileUploaderBinder) service;\r
-            } else {\r
-                return;\r
-            }\r
-            FileDetailFragment fragment = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);\r
-            if (fragment != null)\r
-                fragment.updateFileDetails(false);   // let the fragment gets the mDownloadBinder through getDownloadBinder() (see FileDetailFragment#updateFileDetais())\r
-        }\r
-\r
-        @Override\r
-        public void onServiceDisconnected(ComponentName component) {\r
-            if (component.equals(new ComponentName(FileDetailActivity.this, FileDownloader.class))) {\r
-                Log.d(TAG, "Download service disconnected");\r
-                mDownloaderBinder = null;\r
-            } else if (component.equals(new ComponentName(FileDetailActivity.this, FileUploader.class))) {\r
-                Log.d(TAG, "Upload service disconnected");\r
-                mUploaderBinder = null;\r
-            }\r
-        }\r
-    };    \r
-    \r
-\r
-    @Override\r
-    public void onDestroy() {\r
-        super.onDestroy();\r
-        if (mDownloadConnection != null) {\r
-            unbindService(mDownloadConnection);\r
-            mDownloadConnection = null;\r
-        }\r
-        if (mUploadConnection != null) {\r
-            unbindService(mUploadConnection);\r
-            mUploadConnection = null;\r
-        }\r
-    }\r
-    \r
-    \r
-    @Override\r
-    public boolean onOptionsItemSelected(MenuItem item) {\r
-        boolean returnValue = false;\r
-        \r
-        switch(item.getItemId()){\r
-        case android.R.id.home:\r
-            backToDisplayActivity();\r
-            returnValue = true;\r
-            break;\r
-        default:\r
-               returnValue = super.onOptionsItemSelected(item);\r
-        }\r
-        \r
-        return returnValue;\r
-    }\r
-\r
-\r
-\r
-    @Override\r
-    protected void onResume() {\r
-        \r
-        super.onResume();\r
-        if (!mConfigurationChangedToLandscape) { \r
-            FileDetailFragment fragment = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);\r
-            fragment.updateFileDetails(false);\r
-        }\r
-    }\r
-    \r
-\r
-    private void backToDisplayActivity() {\r
-        Intent intent = new Intent(this, FileDisplayActivity.class);\r
-        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);\r
-        intent.putExtra(FileDetailFragment.EXTRA_FILE, getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE));\r
-        intent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, getIntent().getParcelableExtra(FileDetailFragment.EXTRA_ACCOUNT));\r
-        startActivity(intent);\r
-        finish();\r
-    }\r
-    \r
-    \r
-    @Override\r
-    protected Dialog onCreateDialog(int id) {\r
-        Dialog dialog = null;\r
-        switch (id) {\r
-        case DIALOG_SHORT_WAIT: {\r
-            ProgressDialog working_dialog = new ProgressDialog(this);\r
-            working_dialog.setMessage(getResources().getString(\r
-                    R.string.wait_a_moment));\r
-            working_dialog.setIndeterminate(true);\r
-            working_dialog.setCancelable(false);\r
-            dialog = working_dialog;\r
-            break;\r
-        }\r
-        default:\r
-            dialog = null;\r
-        }\r
-        return dialog;\r
-    }\r
-    \r
-    \r
-    /**\r
-     * {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void onFileStateChanged() {\r
-        // nothing to do here!\r
-    }\r
-\r
-    \r
-    /**\r
-     * {@inheritDoc}\r
-     */\r
-    @Override\r
-    public FileDownloaderBinder getFileDownloaderBinder() {\r
-        return mDownloaderBinder;\r
-    }\r
-\r
-\r
-    @Override\r
-    public FileUploaderBinder getFileUploaderBinder() {\r
-        return mUploaderBinder;\r
-    }\r
-    \r
-}\r
+/* ownCloud Android client application
+ *   Copyright (C) 2011  Bartek Przybylski
+ *   Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License 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.ui.activity;
+
+import android.accounts.Account;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.actionbarsherlock.view.MenuItem;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.services.FileDownloader;
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
+import com.owncloud.android.ui.fragment.FileDetailFragment;
+import com.owncloud.android.ui.fragment.FileFragment;
+import com.owncloud.android.ui.preview.PreviewMediaFragment;
+import com.owncloud.android.AccountUtils;
+import com.owncloud.android.Log_OC;
+
+import com.owncloud.android.R;
+
+/**
+ * This activity displays the details of a file like its name, its size and so
+ * on.
+ * 
+ * @author Bartek Przybylski
+ * @author David A. Velasco
+ */
+public class FileDetailActivity extends SherlockFragmentActivity implements FileFragment.ContainerActivity {
+    
+    public static final int DIALOG_SHORT_WAIT = 0;
+
+    public static final String TAG = FileDetailActivity.class.getSimpleName();
+    
+    public static final String EXTRA_MODE = "MODE";
+    public static final int MODE_DETAILS = 0;
+    public static final int MODE_PREVIEW = 1;
+
+    public static final String KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW";
+    
+    private boolean mConfigurationChangedToLandscape = false;
+    private FileDownloaderBinder mDownloaderBinder = null;
+    private ServiceConnection mDownloadConnection, mUploadConnection = null;
+    private FileUploaderBinder mUploaderBinder = null;
+    private boolean mWaitingToPreview;
+    
+    private OCFile mFile;
+    private Account mAccount;
+
+    private FileDataStorageManager mStorageManager;
+    private DownloadFinishReceiver mDownloadFinishReceiver;
+    
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mFile = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE);
+        mAccount = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_ACCOUNT);
+        mStorageManager = new FileDataStorageManager(mAccount, getContentResolver());
+        
+        // check if configuration changed to large-land ; for a tablet being changed from portrait to landscape when in FileDetailActivity 
+        Configuration conf = getResources().getConfiguration();
+        mConfigurationChangedToLandscape = (conf.orientation == Configuration.ORIENTATION_LANDSCAPE && 
+                                                (conf.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE
+                                           );
+
+        if (!mConfigurationChangedToLandscape) {
+            setContentView(R.layout.file_activity_details);
+        
+            ActionBar actionBar = getSupportActionBar();
+            actionBar.setDisplayHomeAsUpEnabled(true);
+
+            if (savedInstanceState == null) {
+                mWaitingToPreview = false;
+                createChildFragment();
+            } else {
+                mWaitingToPreview = savedInstanceState.getBoolean(KEY_WAITING_TO_PREVIEW);
+            }
+            
+            mDownloadConnection = new DetailsServiceConnection();
+            bindService(new Intent(this, FileDownloader.class), mDownloadConnection, Context.BIND_AUTO_CREATE);
+            mUploadConnection = new DetailsServiceConnection();
+            bindService(new Intent(this, FileUploader.class), mUploadConnection, Context.BIND_AUTO_CREATE);
+            
+            
+        }  else {
+            backToDisplayActivity(false);   // the 'back' won't be effective until this.onStart() and this.onResume() are completed;
+        }
+        
+    }
+    
+    /**
+     * Creates the proper fragment depending upon the state of the handled {@link OCFile} and
+     * the requested {@link Intent}.
+     */
+    private void createChildFragment() {
+        int mode = getIntent().getIntExtra(EXTRA_MODE, MODE_PREVIEW); 
+        
+        Fragment newFragment = null;
+        if (PreviewMediaFragment.canBePreviewed(mFile) && mode == MODE_PREVIEW) {
+            if (mFile.isDown()) {
+                newFragment = new PreviewMediaFragment(mFile, mAccount);
+            
+            } else {
+                newFragment = new FileDetailFragment(mFile, mAccount);
+                mWaitingToPreview = true;
+            }
+            
+        } else {
+            newFragment = new FileDetailFragment(mFile, mAccount);
+        }
+        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+        ft.replace(R.id.fragment, newFragment, FileDetailFragment.FTAG);
+        ft.commit();
+    }
+    
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putBoolean(KEY_WAITING_TO_PREVIEW, mWaitingToPreview);
+    }
+    
+    
+    @Override
+    public void onPause() {
+        super.onPause();
+        if (mDownloadFinishReceiver != null) {
+            unregisterReceiver(mDownloadFinishReceiver);
+            mDownloadFinishReceiver = null;
+        }
+    }
+
+    
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (!mConfigurationChangedToLandscape) {
+            // TODO this is probably unnecessary
+            Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
+            if (fragment != null && fragment instanceof FileDetailFragment) {
+                ((FileDetailFragment) fragment).updateFileDetails(false, false);
+            }
+        }
+        // Listen for download messages
+        IntentFilter downloadIntentFilter = new IntentFilter(FileDownloader.DOWNLOAD_ADDED_MESSAGE);
+        downloadIntentFilter.addAction(FileDownloader.DOWNLOAD_FINISH_MESSAGE);
+        mDownloadFinishReceiver = new DownloadFinishReceiver();
+        registerReceiver(mDownloadFinishReceiver, downloadIntentFilter);
+    }
+    
+    
+    /** Defines callbacks for service binding, passed to bindService() */
+    private class DetailsServiceConnection implements ServiceConnection {
+
+        @Override
+        public void onServiceConnected(ComponentName component, IBinder service) {
+                
+            if (component.equals(new ComponentName(FileDetailActivity.this, FileDownloader.class))) {
+                Log_OC.d(TAG, "Download service connected");
+                mDownloaderBinder = (FileDownloaderBinder) service;
+                if (mWaitingToPreview) {
+                    requestForDownload();
+                }
+                    
+            } else if (component.equals(new ComponentName(FileDetailActivity.this, FileUploader.class))) {
+                Log_OC.d(TAG, "Upload service connected");
+                mUploaderBinder = (FileUploaderBinder) service;
+            } else {
+                return;
+            }
+            
+            Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
+            FileDetailFragment detailsFragment = (fragment instanceof FileDetailFragment) ? (FileDetailFragment) fragment : null;
+            if (detailsFragment != null) {
+                detailsFragment.listenForTransferProgress();
+                detailsFragment.updateFileDetails(mWaitingToPreview, false);   // let the fragment gets the mDownloadBinder through getDownloadBinder() (see FileDetailFragment#updateFileDetais())
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName component) {
+            if (component.equals(new ComponentName(FileDetailActivity.this, FileDownloader.class))) {
+                Log_OC.d(TAG, "Download service disconnected");
+                mDownloaderBinder = null;
+            } else if (component.equals(new ComponentName(FileDetailActivity.this, FileUploader.class))) {
+                Log_OC.d(TAG, "Upload service disconnected");
+                mUploaderBinder = null;
+            }
+        }
+    };    
+    
+    
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mDownloadConnection != null) {
+            unbindService(mDownloadConnection);
+            mDownloadConnection = null;
+        }
+        if (mUploadConnection != null) {
+            unbindService(mUploadConnection);
+            mUploadConnection = null;
+        }
+    }
+    
+    
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        boolean returnValue = false;
+        
+        switch(item.getItemId()){
+        case android.R.id.home:
+            backToDisplayActivity(true);
+            returnValue = true;
+            break;
+        default:
+            returnValue = super.onOptionsItemSelected(item);
+        }
+        
+        return returnValue;
+    }
+
+    private void backToDisplayActivity(boolean moveToParent) {
+        Intent intent = new Intent(this, FileDisplayActivity.class);
+        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        OCFile targetFile = null;
+        if (mFile != null) {
+            targetFile = moveToParent ? mStorageManager.getFileById(mFile.getParentId()) : mFile;
+        }
+        intent.putExtra(FileDetailFragment.EXTRA_FILE, targetFile);
+        intent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, mAccount);
+        startActivity(intent);
+        finish();
+    }
+    
+    
+    @Override
+    protected Dialog onCreateDialog(int id) {
+        Dialog dialog = null;
+        switch (id) {
+        case DIALOG_SHORT_WAIT: {
+            ProgressDialog working_dialog = new ProgressDialog(this);
+            working_dialog.setMessage(getResources().getString(
+                    R.string.wait_a_moment));
+            working_dialog.setIndeterminate(true);
+            working_dialog.setCancelable(false);
+            dialog = working_dialog;
+            break;
+        }
+        default:
+            dialog = null;
+        }
+        return dialog;
+    }
+    
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onFileStateChanged() {
+        // nothing to do here!
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public FileDownloaderBinder getFileDownloaderBinder() {
+        return mDownloaderBinder;
+    }
+
+
+    @Override
+    public FileUploaderBinder getFileUploaderBinder() {
+        return mUploaderBinder;
+    }
+
+
+    @Override
+    public void showFragmentWithDetails(OCFile file) {
+        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+        transaction.replace(R.id.fragment, new FileDetailFragment(file, mAccount), FileDetailFragment.FTAG); 
+        transaction.commit();
+    }
+
+    
+    private void requestForDownload() {
+        if (!mDownloaderBinder.isDownloading(mAccount, mFile)) {
+            Intent i = new Intent(this, FileDownloader.class);
+            i.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount);
+            i.putExtra(FileDownloader.EXTRA_FILE, mFile);
+            startService(i);
+        }
+    }
+
+    
+    /**
+     * Class waiting for broadcast events from the {@link FielDownloader} service.
+     * 
+     * Updates the UI when a download is started or finished, provided that it is relevant for the
+     * current file.
+     */
+    private class DownloadFinishReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            boolean sameAccount = isSameAccount(context, intent);
+            String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
+            boolean samePath = (mFile != null && mFile.getRemotePath().equals(downloadedRemotePath));
+            
+            if (sameAccount && samePath) {
+                updateChildFragment(intent.getAction(), downloadedRemotePath, intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false));
+            }
+            
+            removeStickyBroadcast(intent);
+        }
+
+        private boolean isSameAccount(Context context, Intent intent) {
+            String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME);
+            return (accountName != null && accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name));
+        }
+    }
+
+
+    public void updateChildFragment(String downloadEvent, String downloadedRemotePath, boolean success) {
+        Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
+        if (fragment != null && fragment instanceof FileDetailFragment) {
+            FileDetailFragment detailsFragment = (FileDetailFragment) fragment;
+            OCFile fileInFragment = detailsFragment.getFile();
+            if (fileInFragment != null && !downloadedRemotePath.equals(fileInFragment.getRemotePath())) {
+                // this never should happen; fileInFragment should be always equals to mFile, that was compared to downloadedRemotePath in DownloadReceiver 
+                mWaitingToPreview = false;
+                
+            } else if (downloadEvent.equals(FileDownloader.DOWNLOAD_ADDED_MESSAGE)) {
+                // grants that the progress bar is updated
+                detailsFragment.listenForTransferProgress();
+                detailsFragment.updateFileDetails(true, false);
+                
+            } else if (downloadEvent.equals(FileDownloader.DOWNLOAD_FINISH_MESSAGE)) {
+                //  refresh the details fragment 
+                if (success && mWaitingToPreview) {
+                    mFile = mStorageManager.getFileById(mFile.getFileId());   // update the file from database, for the local storage path
+                    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+                    transaction.replace(R.id.fragment, new PreviewMediaFragment(mFile, mAccount), FileDetailFragment.FTAG);
+                    transaction.commit();
+                    mWaitingToPreview = false;
+                    
+                } else {
+                    detailsFragment.updateFileDetails(false, (success));
+                    // TODO error message if !success Â¿?
+                }
+            }
+        } // TODO else if (fragment != null && fragment )
+        
+    }
+
+}
index c4cbe97..6fd51a9 100644 (file)
-/* ownCloud Android client application\r
- *   Copyright (C) 2011  Bartek Przybylski\r
- *   Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
- *\r
- *   This program is distributed in the hope that it will be useful,\r
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- *   GNU General Public License for more details.\r
- *\r
- *   You should have received a copy of the GNU General Public License\r
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-\r
-package com.owncloud.android.ui.activity;\r
-\r
-import java.io.File;\r
-\r
-import android.accounts.Account;\r
-import android.app.AlertDialog;\r
-import android.app.ProgressDialog;\r
-import android.app.AlertDialog.Builder;\r
-import android.app.Dialog;\r
-import android.content.BroadcastReceiver;\r
-import android.content.ComponentName;\r
-import android.content.ContentResolver;\r
-import android.content.Context;\r
-import android.content.DialogInterface;\r
-import android.content.DialogInterface.OnClickListener;\r
-import android.content.Intent;\r
-import android.content.IntentFilter;\r
-import android.content.ServiceConnection;\r
-import android.content.SharedPreferences;\r
-import android.content.SharedPreferences.Editor;\r
-import android.content.pm.PackageInfo;\r
-import android.content.pm.PackageManager.NameNotFoundException;\r
-import android.content.res.Resources.NotFoundException;\r
-import android.database.Cursor;\r
-import android.graphics.Bitmap;\r
-import android.graphics.drawable.BitmapDrawable;\r
-import android.net.Uri;\r
-import android.os.Bundle;\r
-import android.os.Handler;\r
-import android.os.IBinder;\r
-import android.preference.PreferenceManager;\r
-import android.provider.MediaStore;\r
-import android.support.v4.app.FragmentTransaction;\r
-import android.util.Log;\r
-import android.view.View;\r
-import android.view.ViewGroup;\r
-import android.widget.ArrayAdapter;\r
-import android.widget.EditText;\r
-import android.widget.TextView;\r
-import android.widget.Toast;\r
-\r
-import com.actionbarsherlock.app.ActionBar;\r
-import com.actionbarsherlock.app.ActionBar.OnNavigationListener;\r
-import com.actionbarsherlock.app.SherlockFragmentActivity;\r
-import com.actionbarsherlock.view.Menu;\r
-import com.actionbarsherlock.view.MenuInflater;\r
-import com.actionbarsherlock.view.MenuItem;\r
-import com.actionbarsherlock.view.Window;\r
-import com.owncloud.android.AccountUtils;\r
-import com.owncloud.android.authentication.AccountAuthenticator;\r
-import com.owncloud.android.datamodel.DataStorageManager;\r
-import com.owncloud.android.datamodel.FileDataStorageManager;\r
-import com.owncloud.android.datamodel.OCFile;\r
-import com.owncloud.android.files.services.FileDownloader;\r
-import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;\r
-import com.owncloud.android.files.services.FileObserverService;\r
-import com.owncloud.android.files.services.FileUploader;\r
-import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;\r
-import com.owncloud.android.operations.CreateFolderOperation;\r
-import com.owncloud.android.operations.OnRemoteOperationListener;\r
-import com.owncloud.android.operations.RemoteOperation;\r
-import com.owncloud.android.operations.RemoteOperationResult;\r
-import com.owncloud.android.operations.RemoveFileOperation;\r
-import com.owncloud.android.operations.RenameFileOperation;\r
-import com.owncloud.android.operations.SynchronizeFileOperation;\r
-import com.owncloud.android.operations.RemoteOperationResult.ResultCode;\r
-import com.owncloud.android.syncadapter.FileSyncService;\r
-import com.owncloud.android.ui.dialog.ChangelogDialog;\r
-import com.owncloud.android.ui.dialog.SslValidatorDialog;\r
-import com.owncloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener;\r
-import com.owncloud.android.ui.fragment.FileDetailFragment;\r
-import com.owncloud.android.ui.fragment.OCFileListFragment;\r
-\r
-import com.owncloud.android.R;\r
-\r
-/**\r
- * Displays, what files the user has available in his ownCloud.\r
- * \r
- * @author Bartek Przybylski\r
- * \r
- */\r
-\r
-public class FileDisplayActivity extends SherlockFragmentActivity implements\r
-    OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNavigationListener, OnSslValidatorListener, OnRemoteOperationListener {\r
-    \r
-    private ArrayAdapter<String> mDirectories;\r
-    private OCFile mCurrentDir = null;\r
-    private OCFile mCurrentFile = null;\r
-\r
-    private DataStorageManager mStorageManager;\r
-    private SyncBroadcastReceiver mSyncBroadcastReceiver;\r
-    private UploadFinishReceiver mUploadFinishReceiver;\r
-    private DownloadFinishReceiver mDownloadFinishReceiver;\r
-    private FileDownloaderBinder mDownloaderBinder = null;\r
-    private FileUploaderBinder mUploaderBinder = null;\r
-    private ServiceConnection mDownloadConnection = null, mUploadConnection = null;\r
-    private RemoteOperationResult mLastSslUntrustedServerResult = null;\r
-    \r
-    private OCFileListFragment mFileList;\r
-    \r
-    private boolean mDualPane;\r
-    private Handler mHandler;\r
-    \r
-    private static final int DIALOG_SETUP_ACCOUNT = 0;\r
-    private static final int DIALOG_CREATE_DIR = 1;\r
-    private static final int DIALOG_ABOUT_APP = 2;\r
-    public static final int DIALOG_SHORT_WAIT = 3;\r
-    private static final int DIALOG_CHOOSE_UPLOAD_SOURCE = 4;\r
-    private static final int DIALOG_SSL_VALIDATOR = 5;\r
-    private static final int DIALOG_CERT_NOT_SAVED = 6;\r
-    private static final String DIALOG_CHANGELOG_TAG = "DIALOG_CHANGELOG";\r
-\r
-    \r
-    private static final int ACTION_SELECT_CONTENT_FROM_APPS = 1;\r
-    private static final int ACTION_SELECT_MULTIPLE_FILES = 2;\r
-    \r
-    private static final String TAG = "FileDisplayActivity";\r
-\r
-    private static int[] mMenuIdentifiersToPatch = {R.id.about_app};\r
-    \r
-    @Override\r
-    public void onCreate(Bundle savedInstanceState) {\r
-        Log.d(getClass().toString(), "onCreate() start");\r
-        super.onCreate(savedInstanceState);\r
-        \r
-        mHandler = new Handler();\r
-\r
-        /// Load of parameters from received intent\r
-        Account account = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_ACCOUNT);\r
-        if (account != null && AccountUtils.setCurrentOwnCloudAccount(this, account.name)) {\r
-            mCurrentDir = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE); \r
-        }\r
-        \r
-        /// Load of saved instance state: keep this always before initDataFromCurrentAccount()\r
-        if(savedInstanceState != null) {\r
-            // TODO - test if savedInstanceState should take precedence over file in the intent ALWAYS (now), NEVER, or SOME TIMES\r
-            mCurrentDir = savedInstanceState.getParcelable(FileDetailFragment.EXTRA_FILE);\r
-        }\r
-        \r
-        if (!AccountUtils.accountsAreSetup(this)) {\r
-            /// no account available: FORCE ACCOUNT CREATION\r
-            mStorageManager = null;\r
-            createFirstAccount();\r
-            \r
-        } else {    /// at least an account is available\r
-            \r
-            initDataFromCurrentAccount();   // it checks mCurrentDir and mCurrentFile with the current account\r
-            \r
-        }\r
-        \r
-        mUploadConnection = new ListServiceConnection(); \r
-        mDownloadConnection = new ListServiceConnection();\r
-        bindService(new Intent(this, FileUploader.class), mUploadConnection, Context.BIND_AUTO_CREATE);\r
-        bindService(new Intent(this, FileDownloader.class), mDownloadConnection, Context.BIND_AUTO_CREATE);\r
-\r
-        // PIN CODE request ;  best location is to decide, let's try this first\r
-        if (getIntent().getAction() != null && getIntent().getAction().equals(Intent.ACTION_MAIN) && savedInstanceState == null) {\r
-            requestPinCode();\r
-        }\r
-\r
-        // file observer\r
-        Intent observer_intent = new Intent(this, FileObserverService.class);\r
-        observer_intent.putExtra(FileObserverService.KEY_FILE_CMD, FileObserverService.CMD_INIT_OBSERVED_LIST);\r
-        startService(observer_intent);\r
-        \r
-            \r
-        /// USER INTERFACE\r
-        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);\r
-            \r
-        // Drop-down navigation \r
-        mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);\r
-        OCFile currFile = mCurrentDir;\r
-        while(mStorageManager != null && currFile != null && currFile.getFileName() != OCFile.PATH_SEPARATOR) {\r
-            mDirectories.add(currFile.getFileName());\r
-            currFile = mStorageManager.getFileById(currFile.getParentId());\r
-        }\r
-        mDirectories.add(OCFile.PATH_SEPARATOR);\r
-\r
-        // Inflate and set the layout view\r
-        setContentView(R.layout.files);    \r
-        mFileList = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);\r
-        mDualPane = (findViewById(R.id.file_details_container) != null);\r
-        if (mDualPane) {\r
-            initFileDetailsInDualPane();\r
-        }\r
-            \r
-        // Action bar setup\r
-        ActionBar actionBar = getSupportActionBar();\r
-        actionBar.setHomeButtonEnabled(true);   // mandatory since Android ICS, according to the official documentation\r
-        actionBar.setDisplayHomeAsUpEnabled(mCurrentDir != null && mCurrentDir.getParentId() != 0);\r
-        actionBar.setDisplayShowTitleEnabled(false);\r
-        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);\r
-        actionBar.setListNavigationCallbacks(mDirectories, this);\r
-        setSupportProgressBarIndeterminateVisibility(false);        // always AFTER setContentView(...) ; to workaround bug in its implementation\r
-        \r
-        \r
-        // show changelog, if needed\r
-        //showChangeLog();\r
-        \r
-        Log.d(getClass().toString(), "onCreate() end");\r
-    }\r
-\r
-    \r
-    /**\r
-     * Shows a dialog with the change log of the current version after each app update\r
-     * \r
-     *  TODO make it permanent; by now, only to advice the workaround app for 4.1.x\r
-     */\r
-    private void showChangeLog() {\r
-        if (android.os.Build.VERSION.SDK_INT == android.os.Build.VERSION_CODES.JELLY_BEAN) {\r
-            final String KEY_VERSION = "version";\r
-            SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());\r
-            int currentVersionNumber = 0;\r
-            int savedVersionNumber = sharedPref.getInt(KEY_VERSION, 0);\r
-            try {\r
-                PackageInfo pi          = getPackageManager().getPackageInfo(getPackageName(), 0);\r
-                currentVersionNumber    = pi.versionCode;\r
-            } catch (Exception e) {}\r
-     \r
-            if (currentVersionNumber > savedVersionNumber) {\r
-                ChangelogDialog.newInstance(true).show(getSupportFragmentManager(), DIALOG_CHANGELOG_TAG);\r
-                Editor editor   = sharedPref.edit();\r
-                editor.putInt(KEY_VERSION, currentVersionNumber);\r
-                editor.commit();\r
-            }\r
-        }\r
-    }\r
-    \r
-\r
-    /**\r
-     * Launches the account creation activity. To use when no ownCloud account is available\r
-     */\r
-    private void createFirstAccount() {\r
-        Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT);\r
-        intent.putExtra(android.provider.Settings.EXTRA_AUTHORITIES, new String[] { AccountAuthenticator.AUTHORITY });\r
-        startActivity(intent);  // the new activity won't be created until this.onStart() and this.onResume() are finished;\r
-    }\r
-\r
-\r
-    /**\r
-     *  Load of state dependent of the existence of an ownCloud account\r
-     */\r
-    private void initDataFromCurrentAccount() {\r
-        /// Storage manager initialization - access to local database\r
-        mStorageManager = new FileDataStorageManager(\r
-                AccountUtils.getCurrentOwnCloudAccount(this),\r
-                getContentResolver());\r
-\r
-        /// Check if mCurrentDir is a directory\r
-        if(mCurrentDir != null && !mCurrentDir.isDirectory()) {\r
-            mCurrentFile = mCurrentDir;\r
-            mCurrentDir = mStorageManager.getFileById(mCurrentDir.getParentId());\r
-        }\r
-        \r
-        /// Check if mCurrentDir and mCurrentFile are in the current account, and update them\r
-        if (mCurrentDir != null) {\r
-            mCurrentDir = mStorageManager.getFileByPath(mCurrentDir.getRemotePath());   // mCurrentDir == null if it is not in the current account\r
-        }\r
-        if (mCurrentFile != null) {\r
-            if (mCurrentFile.fileExists()) {\r
-                mCurrentFile = mStorageManager.getFileByPath(mCurrentFile.getRemotePath());   // mCurrentFile == null if it is not in the current account\r
-            }   // else : keep mCurrentFile with the received value; this is currently the case of an upload in progress, when the user presses the status notification in a landscape tablet\r
-        }\r
-        \r
-        /// Default to root if mCurrentDir was not found\r
-        if (mCurrentDir == null) {\r
-            mCurrentDir = mStorageManager.getFileByPath("/"); // will be NULL if the database was never synchronized\r
-        }\r
-    }\r
-        \r
-    \r
-    private void initFileDetailsInDualPane() {\r
-        if (mDualPane && getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG) == null) {\r
-            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();\r
-            if (mCurrentFile != null) {\r
-                transaction.replace(R.id.file_details_container, new FileDetailFragment(mCurrentFile, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG); // empty FileDetailFragment\r
-                mCurrentFile = null;\r
-            } else {\r
-                transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null), FileDetailFragment.FTAG); // empty FileDetailFragment\r
-            }\r
-            transaction.commit();\r
-        }\r
-    }\r
-    \r
-    \r
-    @Override\r
-    public void onDestroy() {\r
-        super.onDestroy();\r
-        if (mDownloadConnection != null)\r
-            unbindService(mDownloadConnection);\r
-        if (mUploadConnection != null)\r
-            unbindService(mUploadConnection);\r
-    }\r
-\r
-    \r
-    @Override\r
-    public boolean onCreateOptionsMenu(Menu menu) {\r
-        MenuInflater inflater = getSherlock().getMenuInflater();\r
-            inflater.inflate(R.menu.menu, menu);\r
-            \r
-            patchHiddenAccents(menu);\r
-            \r
-            return true;\r
-    }\r
-\r
-    /**\r
-     * Workaround for this: <a href="http://code.google.com/p/android/issues/detail?id=3974">http://code.google.com/p/android/issues/detail?id=3974</a> \r
-     * \r
-     * @param menu      Menu to patch\r
-     */\r
-    private void patchHiddenAccents(Menu menu) {\r
-        for (int i = 0; i < mMenuIdentifiersToPatch.length ; i++) {\r
-            MenuItem aboutItem = menu.findItem(mMenuIdentifiersToPatch[i]);\r
-            if (aboutItem != null && aboutItem.getIcon() instanceof BitmapDrawable) {\r
-                // Clip off the bottom three (density independent) pixels of transparent padding\r
-                Bitmap original = ((BitmapDrawable) aboutItem.getIcon()).getBitmap();\r
-                float scale = getResources().getDisplayMetrics().density;\r
-                int clippedHeight = (int) (original.getHeight() - (3 * scale));\r
-                Bitmap scaled = Bitmap.createBitmap(original, 0, 0, original.getWidth(), clippedHeight);\r
-                aboutItem.setIcon(new BitmapDrawable(getResources(), scaled));\r
-            }\r
-        }\r
-    }\r
-\r
-\r
-    @Override\r
-    public boolean onOptionsItemSelected(MenuItem item) {\r
-        boolean retval = true;\r
-        switch (item.getItemId()) {\r
-            case R.id.createDirectoryItem: {\r
-                showDialog(DIALOG_CREATE_DIR);\r
-                break;\r
-            }\r
-            case R.id.startSync: {\r
-                startSynchronization();\r
-                break;\r
-            }\r
-            case R.id.action_upload: {\r
-                showDialog(DIALOG_CHOOSE_UPLOAD_SOURCE);\r
-                break;\r
-            }\r
-            case R.id.action_settings: {\r
-                Intent settingsIntent = new Intent(this, Preferences.class);\r
-                startActivity(settingsIntent);\r
-                break;\r
-            }\r
-            case R.id.about_app : {\r
-                showDialog(DIALOG_ABOUT_APP);\r
-                break;\r
-            }\r
-            case android.R.id.home: {\r
-                if(mCurrentDir != null && mCurrentDir.getParentId() != 0){\r
-                    onBackPressed(); \r
-                }\r
-                break;\r
-            }\r
-            default:\r
-                retval = super.onOptionsItemSelected(item);\r
-        }\r
-        return retval;\r
-    }\r
-\r
-    private void startSynchronization() {\r
-        ContentResolver.cancelSync(null, AccountAuthenticator.AUTHORITY);   // cancel the current synchronizations of any ownCloud account\r
-        Bundle bundle = new Bundle();\r
-        bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);\r
-        ContentResolver.requestSync(\r
-                AccountUtils.getCurrentOwnCloudAccount(this),\r
-                AccountAuthenticator.AUTHORITY, bundle);\r
-    }\r
-\r
-\r
-    @Override\r
-    public boolean onNavigationItemSelected(int itemPosition, long itemId) {\r
-        int i = itemPosition;\r
-        while (i-- != 0) {\r
-            onBackPressed();\r
-        }\r
-        // the next operation triggers a new call to this method, but it's necessary to \r
-        // ensure that the name exposed in the action bar is the current directory when the \r
-        // user selected it in the navigation list\r
-        if (itemPosition != 0)\r
-            getSupportActionBar().setSelectedNavigationItem(0);\r
-        return true;\r
-    }\r
-\r
-    /**\r
-     * Called, when the user selected something for uploading\r
-     */\r
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {\r
-        \r
-        if (requestCode == ACTION_SELECT_CONTENT_FROM_APPS && (resultCode == RESULT_OK || resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)) {\r
-            requestSimpleUpload(data, resultCode);\r
-            \r
-        } else if (requestCode == ACTION_SELECT_MULTIPLE_FILES && (resultCode == RESULT_OK || resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)) {\r
-            requestMultipleUpload(data, resultCode);\r
-            \r
-        }\r
-    }\r
-\r
-    private void requestMultipleUpload(Intent data, int resultCode) {\r
-        String[] filePaths = data.getStringArrayExtra(UploadFilesActivity.EXTRA_CHOSEN_FILES);\r
-        if (filePaths != null) {\r
-            String[] remotePaths = new String[filePaths.length];\r
-            String remotePathBase = "";\r
-            for (int j = mDirectories.getCount() - 2; j >= 0; --j) {\r
-                remotePathBase += OCFile.PATH_SEPARATOR + mDirectories.getItem(j);\r
-            }\r
-            if (!remotePathBase.endsWith(OCFile.PATH_SEPARATOR))\r
-                remotePathBase += OCFile.PATH_SEPARATOR;\r
-            for (int j = 0; j< remotePaths.length; j++) {\r
-                remotePaths[j] = remotePathBase + (new File(filePaths[j])).getName();\r
-            }\r
-\r
-            Intent i = new Intent(this, FileUploader.class);\r
-            i.putExtra(FileUploader.KEY_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));\r
-            i.putExtra(FileUploader.KEY_LOCAL_FILE, filePaths);\r
-            i.putExtra(FileUploader.KEY_REMOTE_FILE, remotePaths);\r
-            i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES);\r
-            if (resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)\r
-                i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);\r
-            startService(i);\r
-            \r
-        } else {\r
-            Log.d("FileDisplay", "User clicked on 'Update' with no selection");\r
-            Toast t = Toast.makeText(this, getString(R.string.filedisplay_no_file_selected), Toast.LENGTH_LONG);\r
-            t.show();\r
-            return;\r
-        }\r
-    }\r
-\r
-\r
-    private void requestSimpleUpload(Intent data, int resultCode) {\r
-        String filepath = null;\r
-        try {\r
-            Uri selectedImageUri = data.getData();\r
-\r
-            String filemanagerstring = selectedImageUri.getPath();\r
-            String selectedImagePath = getPath(selectedImageUri);\r
-\r
-            if (selectedImagePath != null)\r
-                filepath = selectedImagePath;\r
-            else\r
-                filepath = filemanagerstring;\r
-            \r
-        } catch (Exception e) {\r
-            Log.e("FileDisplay", "Unexpected exception when trying to read the result of Intent.ACTION_GET_CONTENT", e);\r
-            e.printStackTrace();\r
-            \r
-        } finally {\r
-            if (filepath == null) {\r
-                Log.e("FileDisplay", "Couldnt resolve path to file");\r
-                Toast t = Toast.makeText(this, getString(R.string.filedisplay_unexpected_bad_get_content), Toast.LENGTH_LONG);\r
-                t.show();\r
-                return;\r
-            }\r
-        }\r
-\r
-        Intent i = new Intent(this, FileUploader.class);\r
-        i.putExtra(FileUploader.KEY_ACCOUNT,\r
-                AccountUtils.getCurrentOwnCloudAccount(this));\r
-        String remotepath = new String();\r
-        for (int j = mDirectories.getCount() - 2; j >= 0; --j) {\r
-            remotepath += OCFile.PATH_SEPARATOR + mDirectories.getItem(j);\r
-        }\r
-        if (!remotepath.endsWith(OCFile.PATH_SEPARATOR))\r
-            remotepath += OCFile.PATH_SEPARATOR;\r
-        remotepath += new File(filepath).getName();\r
-\r
-        i.putExtra(FileUploader.KEY_LOCAL_FILE, filepath);\r
-        i.putExtra(FileUploader.KEY_REMOTE_FILE, remotepath);\r
-        i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);\r
-        if (resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)\r
-            i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);\r
-        startService(i);\r
-    }\r
-\r
-\r
-    @Override\r
-    public void onBackPressed() {\r
-        if (mDirectories.getCount() <= 1) {\r
-            finish();\r
-            return;\r
-        }\r
-        popDirname();\r
-        mFileList.onNavigateUp();\r
-        mCurrentDir = mFileList.getCurrentFile();\r
-        \r
-        if (mDualPane) {\r
-            // Resets the FileDetailsFragment on Tablets so that it always displays\r
-            FileDetailFragment fileDetails = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);\r
-            if (fileDetails != null && !fileDetails.isEmpty()) {\r
-                FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();\r
-                transaction.remove(fileDetails);\r
-                transaction.add(R.id.file_details_container, new FileDetailFragment(null, null), FileDetailFragment.FTAG);\r
-                transaction.commit();\r
-            }\r
-        }\r
-        \r
-        if(mCurrentDir.getParentId() == 0){\r
-            ActionBar actionBar = getSupportActionBar(); \r
-            actionBar.setDisplayHomeAsUpEnabled(false);\r
-        } \r
-    }\r
-\r
-    @Override\r
-    protected void onSaveInstanceState(Bundle outState) {\r
-        // responsibility of restore is preferred in onCreate() before than in onRestoreInstanceState when there are Fragments involved\r
-        Log.d(getClass().toString(), "onSaveInstanceState() start");\r
-        super.onSaveInstanceState(outState);\r
-        outState.putParcelable(FileDetailFragment.EXTRA_FILE, mCurrentDir);\r
-        if (mDualPane) {\r
-            FileDetailFragment fragment = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);\r
-            if (fragment != null) {\r
-                OCFile file = fragment.getDisplayedFile();\r
-                if (file != null) {\r
-                    outState.putParcelable(FileDetailFragment.EXTRA_FILE, file);\r
-                }\r
-            }\r
-        }\r
-        Log.d(getClass().toString(), "onSaveInstanceState() end");\r
-    }\r
-\r
-    @Override\r
-    protected void onResume() {\r
-        Log.d(getClass().toString(), "onResume() start");\r
-        super.onResume();\r
-\r
-        if (AccountUtils.accountsAreSetup(this)) {\r
-            \r
-            if (mStorageManager == null) {\r
-                // this is necessary for handling the come back to FileDisplayActivity when the first ownCloud account is created \r
-                initDataFromCurrentAccount();\r
-                if (mDualPane) {\r
-                    initFileDetailsInDualPane();\r
-                }\r
-            }\r
-            \r
-            // Listen for sync messages\r
-            IntentFilter syncIntentFilter = new IntentFilter(FileSyncService.SYNC_MESSAGE);\r
-            mSyncBroadcastReceiver = new SyncBroadcastReceiver();\r
-            registerReceiver(mSyncBroadcastReceiver, syncIntentFilter);\r
-            \r
-            // Listen for upload messages\r
-            IntentFilter uploadIntentFilter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE);\r
-            mUploadFinishReceiver = new UploadFinishReceiver();\r
-            registerReceiver(mUploadFinishReceiver, uploadIntentFilter);\r
-            \r
-            // Listen for download messages\r
-            IntentFilter downloadIntentFilter = new IntentFilter(FileDownloader.DOWNLOAD_FINISH_MESSAGE);\r
-            mDownloadFinishReceiver = new DownloadFinishReceiver();\r
-            registerReceiver(mDownloadFinishReceiver, downloadIntentFilter);\r
-        \r
-            // List current directory\r
-            mFileList.listDirectory(mCurrentDir);   // TODO we should find the way to avoid the need of this (maybe it's not necessary yet; to check)\r
-            \r
-        } else {\r
-            \r
-            mStorageManager = null;     // an invalid object will be there if all the ownCloud accounts are removed\r
-            showDialog(DIALOG_SETUP_ACCOUNT);\r
-            \r
-        }\r
-        Log.d(getClass().toString(), "onResume() end");\r
-    }\r
-\r
-    \r
-    @Override\r
-    protected void onPause() {\r
-        Log.d(getClass().toString(), "onPause() start");\r
-        super.onPause();\r
-        if (mSyncBroadcastReceiver != null) {\r
-            unregisterReceiver(mSyncBroadcastReceiver);\r
-            mSyncBroadcastReceiver = null;\r
-        }\r
-        if (mUploadFinishReceiver != null) {\r
-            unregisterReceiver(mUploadFinishReceiver);\r
-            mUploadFinishReceiver = null;\r
-        }\r
-        if (mDownloadFinishReceiver != null) {\r
-            unregisterReceiver(mDownloadFinishReceiver);\r
-            mDownloadFinishReceiver = null;\r
-        }\r
-        if (!AccountUtils.accountsAreSetup(this)) {\r
-            dismissDialog(DIALOG_SETUP_ACCOUNT);\r
-        }\r
-        \r
-        Log.d(getClass().toString(), "onPause() end");\r
-    }\r
-\r
-    \r
-    @Override\r
-    protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {\r
-        if (id == DIALOG_SSL_VALIDATOR && mLastSslUntrustedServerResult != null) {\r
-            ((SslValidatorDialog)dialog).updateResult(mLastSslUntrustedServerResult);\r
-        }\r
-    }\r
-\r
-    \r
-    @Override\r
-    protected Dialog onCreateDialog(int id) {\r
-        Dialog dialog = null;\r
-        AlertDialog.Builder builder;\r
-        switch (id) {\r
-        case DIALOG_SETUP_ACCOUNT: {\r
-            builder = new AlertDialog.Builder(this);\r
-            builder.setTitle(R.string.main_tit_accsetup);\r
-            builder.setMessage(R.string.main_wrn_accsetup);\r
-            builder.setCancelable(false);\r
-            builder.setPositiveButton(android.R.string.ok, new OnClickListener() {\r
-                public void onClick(DialogInterface dialog, int which) {\r
-                    createFirstAccount();\r
-                    dialog.dismiss();\r
-                }\r
-            });\r
-            String message = String.format(getString(R.string.common_exit), getString(R.string.app_name));\r
-            builder.setNegativeButton(message, new OnClickListener() {\r
-                public void onClick(DialogInterface dialog, int which) {\r
-                    dialog.dismiss();\r
-                    finish();\r
-                }\r
-            });\r
-            //builder.setNegativeButton(android.R.string.cancel, this);\r
-            dialog = builder.create();\r
-            break;\r
-        }\r
-        case DIALOG_ABOUT_APP: {\r
-            builder = new AlertDialog.Builder(this);\r
-            builder.setTitle(getString(R.string.about_title));\r
-            PackageInfo pkg;\r
-            try {\r
-                pkg = getPackageManager().getPackageInfo(getPackageName(), 0);\r
-                builder.setMessage(String.format(getString(R.string.about_message), getString(R.string.app_name), pkg.versionName));\r
-                builder.setIcon(android.R.drawable.ic_menu_info_details);\r
-                dialog = builder.create();\r
-            } catch (NameNotFoundException e) {\r
-                builder = null;\r
-                dialog = null;\r
-                Log.e(TAG, "Error while showing about dialog", e);\r
-            }\r
-            break;\r
-        }\r
-        case DIALOG_CREATE_DIR: {\r
-            builder = new Builder(this);\r
-            final EditText dirNameInput = new EditText(getBaseContext());\r
-            builder.setView(dirNameInput);\r
-            builder.setTitle(R.string.uploader_info_dirname);\r
-            int typed_color = getResources().getColor(R.color.setup_text_typed);\r
-            dirNameInput.setTextColor(typed_color);\r
-            builder.setPositiveButton(android.R.string.ok,\r
-                    new OnClickListener() {\r
-                        public void onClick(DialogInterface dialog, int which) {\r
-                            String directoryName = dirNameInput.getText().toString();\r
-                            if (directoryName.trim().length() == 0) {\r
-                                dialog.cancel();\r
-                                return;\r
-                            }\r
-    \r
-                            // Figure out the path where the dir needs to be created\r
-                            String path;\r
-                            if (mCurrentDir == null) {\r
-                                // this is just a patch; we should ensure that mCurrentDir never is null\r
-                                if (!mStorageManager.fileExists(OCFile.PATH_SEPARATOR)) {\r
-                                    OCFile file = new OCFile(OCFile.PATH_SEPARATOR);\r
-                                    mStorageManager.saveFile(file);\r
-                                }\r
-                                mCurrentDir = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR);\r
-                            }\r
-                            path = FileDisplayActivity.this.mCurrentDir.getRemotePath();\r
-                            \r
-                            // Create directory\r
-                            path += directoryName + OCFile.PATH_SEPARATOR;\r
-                            RemoteOperation operation = new CreateFolderOperation(path, mCurrentDir.getFileId(), mStorageManager);\r
-                            operation.execute(  AccountUtils.getCurrentOwnCloudAccount(FileDisplayActivity.this), \r
-                                                FileDisplayActivity.this, \r
-                                                FileDisplayActivity.this, \r
-                                                mHandler,\r
-                                                FileDisplayActivity.this);\r
-                            \r
-                            dialog.dismiss();\r
-                            \r
-                            showDialog(DIALOG_SHORT_WAIT);\r
-                        }\r
-                    });\r
-            builder.setNegativeButton(R.string.common_cancel,\r
-                    new OnClickListener() {\r
-                        public void onClick(DialogInterface dialog, int which) {\r
-                            dialog.cancel();\r
-                        }\r
-                    });\r
-            dialog = builder.create();\r
-            break;\r
-        }\r
-        case DIALOG_SHORT_WAIT: {\r
-            ProgressDialog working_dialog = new ProgressDialog(this);\r
-            working_dialog.setMessage(getResources().getString(\r
-                    R.string.wait_a_moment));\r
-            working_dialog.setIndeterminate(true);\r
-            working_dialog.setCancelable(false);\r
-            dialog = working_dialog;\r
-            break;\r
-        }\r
-        case DIALOG_CHOOSE_UPLOAD_SOURCE: {\r
-            final String [] items = {   getString(R.string.actionbar_upload_files), \r
-                                        getString(R.string.actionbar_upload_from_apps) }; \r
-            builder = new AlertDialog.Builder(this);\r
-            builder.setTitle(R.string.actionbar_upload);\r
-            builder.setItems(items, new DialogInterface.OnClickListener() {\r
-                public void onClick(DialogInterface dialog, int item) {\r
-                    if (item == 0) {\r
-                        //if (!mDualPane) { \r
-                            Intent action = new Intent(FileDisplayActivity.this, UploadFilesActivity.class);\r
-                            action.putExtra(UploadFilesActivity.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(FileDisplayActivity.this));\r
-                            startActivityForResult(action, ACTION_SELECT_MULTIPLE_FILES);\r
-                        //} else {\r
-                            // TODO create and handle new fragment LocalFileListFragment\r
-                        //}\r
-                    } else if (item == 1) {\r
-                        Intent action = new Intent(Intent.ACTION_GET_CONTENT);\r
-                        action = action.setType("*/*")\r
-                                .addCategory(Intent.CATEGORY_OPENABLE);\r
-                        startActivityForResult(\r
-                                Intent.createChooser(action, getString(R.string.upload_chooser_title)),\r
-                                ACTION_SELECT_CONTENT_FROM_APPS);\r
-                    }\r
-                }\r
-            });\r
-            dialog = builder.create();\r
-            break;\r
-        }\r
-        case DIALOG_SSL_VALIDATOR: {\r
-            dialog = SslValidatorDialog.newInstance(this, mLastSslUntrustedServerResult, this);\r
-            break;\r
-        }\r
-        case DIALOG_CERT_NOT_SAVED: {\r
-            builder = new AlertDialog.Builder(this);\r
-            builder.setMessage(getResources().getString(R.string.ssl_validator_not_saved));\r
-            builder.setCancelable(false);\r
-            builder.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {\r
-                    @Override\r
-                    public void onClick(DialogInterface dialog, int which) {\r
-                        dialog.dismiss();\r
-                    };\r
-                });\r
-            dialog = builder.create();\r
-            break;\r
-        }\r
-        default:\r
-            dialog = null;\r
-        }\r
-    \r
-        return dialog;\r
-    }\r
-\r
-    \r
-    /**\r
-     * Translates a content URI of an image to a physical path\r
-     * on the disk\r
-     * @param uri The URI to resolve\r
-     * @return The path to the image or null if it could not be found\r
-     */\r
-    public String getPath(Uri uri) {\r
-        String[] projection = { MediaStore.Images.Media.DATA };\r
-        Cursor cursor = managedQuery(uri, projection, null, null, null);\r
-        if (cursor != null) {\r
-            int column_index = cursor\r
-                    .getColumnIndexOrThrow(MediaStore.Images.Media.DATA);\r
-            cursor.moveToFirst();\r
-            return cursor.getString(column_index);\r
-        } \r
-        return null;\r
-    }\r
-    \r
-    /**\r
-     * Pushes a directory to the drop down list\r
-     * @param directory to push\r
-     * @throws IllegalArgumentException If the {@link OCFile#isDirectory()} returns false.\r
-     */\r
-    public void pushDirname(OCFile directory) {\r
-        if(!directory.isDirectory()){\r
-            throw new IllegalArgumentException("Only directories may be pushed!");\r
-        }\r
-        mDirectories.insert(directory.getFileName(), 0);\r
-        mCurrentDir = directory;\r
-    }\r
-\r
-    /**\r
-     * Pops a directory name from the drop down list\r
-     * @return True, unless the stack is empty\r
-     */\r
-    public boolean popDirname() {\r
-        mDirectories.remove(mDirectories.getItem(0));\r
-        return !mDirectories.isEmpty();\r
-    }\r
-\r
-\r
-    // Custom array adapter to override text colors\r
-    private class CustomArrayAdapter<T> extends ArrayAdapter<T> {\r
-    \r
-        public CustomArrayAdapter(FileDisplayActivity ctx, int view) {\r
-            super(ctx, view);\r
-        }\r
-    \r
-        public View getView(int position, View convertView, ViewGroup parent) {\r
-            View v = super.getView(position, convertView, parent);\r
-    \r
-            ((TextView) v).setTextColor(getResources().getColorStateList(\r
-                    android.R.color.white));\r
-            return v;\r
-        }\r
-    \r
-        public View getDropDownView(int position, View convertView,\r
-                ViewGroup parent) {\r
-            View v = super.getDropDownView(position, convertView, parent);\r
-    \r
-            ((TextView) v).setTextColor(getResources().getColorStateList(\r
-                    android.R.color.white));\r
-    \r
-            return v;\r
-        }\r
-    \r
-    }\r
-\r
-    private class SyncBroadcastReceiver extends BroadcastReceiver {\r
-\r
-        /**\r
-         * {@link BroadcastReceiver} to enable syncing feedback in UI\r
-         */\r
-        @Override\r
-        public void onReceive(Context context, Intent intent) {\r
-            boolean inProgress = intent.getBooleanExtra(\r
-                    FileSyncService.IN_PROGRESS, false);\r
-            String accountName = intent\r
-                    .getStringExtra(FileSyncService.ACCOUNT_NAME);\r
-\r
-            Log.d("FileDisplay", "sync of account " + accountName\r
-                    + " is in_progress: " + inProgress);\r
-\r
-            if (accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name)) {  \r
-            \r
-                String synchFolderRemotePath = intent.getStringExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH); \r
-                 \r
-                boolean fillBlankRoot = false;\r
-                if (mCurrentDir == null) {\r
-                    mCurrentDir = mStorageManager.getFileByPath("/");\r
-                    fillBlankRoot = (mCurrentDir != null);\r
-                }\r
-\r
-                if ((synchFolderRemotePath != null && mCurrentDir != null && (mCurrentDir.getRemotePath().equals(synchFolderRemotePath)))\r
-                        || fillBlankRoot ) {\r
-                    if (!fillBlankRoot) \r
-                        mCurrentDir = getStorageManager().getFileByPath(synchFolderRemotePath);\r
-                    OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager()\r
-                            .findFragmentById(R.id.fileList);\r
-                    if (fileListFragment != null) {\r
-                        fileListFragment.listDirectory(mCurrentDir);\r
-                    }\r
-                }\r
-                \r
-                setSupportProgressBarIndeterminateVisibility(inProgress);\r
-                removeStickyBroadcast(intent);\r
-                \r
-            }\r
-            \r
-            RemoteOperationResult synchResult = (RemoteOperationResult)intent.getSerializableExtra(FileSyncService.SYNC_RESULT);\r
-            if (synchResult != null) {\r
-                if (synchResult.getCode().equals(RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED)) {\r
-                    mLastSslUntrustedServerResult = synchResult;\r
-                    showDialog(DIALOG_SSL_VALIDATOR); \r
-                }\r
-            }\r
-        }\r
-    }\r
-    \r
-\r
-    private class UploadFinishReceiver extends BroadcastReceiver {\r
-        /**\r
-         * Once the file upload has finished -> update view\r
-         *  @author David A. Velasco\r
-         * {@link BroadcastReceiver} to enable upload feedback in UI\r
-         */\r
-        @Override\r
-        public void onReceive(Context context, Intent intent) {\r
-            String uploadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);\r
-            String accountName = intent.getStringExtra(FileUploader.ACCOUNT_NAME);\r
-            boolean sameAccount = accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name);\r
-            boolean isDescendant = (mCurrentDir != null) && (uploadedRemotePath != null) && (uploadedRemotePath.startsWith(mCurrentDir.getRemotePath()));\r
-            if (sameAccount && isDescendant) {\r
-                OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);\r
-                if (fileListFragment != null) { \r
-                    fileListFragment.listDirectory();\r
-                }\r
-            }\r
-        }\r
-        \r
-    }\r
-    \r
-    \r
-    /**\r
-     * Once the file download has finished -> update view\r
-     */\r
-    private class DownloadFinishReceiver extends BroadcastReceiver {\r
-        @Override\r
-        public void onReceive(Context context, Intent intent) {\r
-            String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);\r
-            String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME);\r
-            boolean sameAccount = accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name);\r
-            boolean isDescendant = (mCurrentDir != null) && (downloadedRemotePath != null) && (downloadedRemotePath.startsWith(mCurrentDir.getRemotePath()));\r
-            if (sameAccount && isDescendant) {\r
-                OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);\r
-                if (fileListFragment != null) { \r
-                    fileListFragment.listDirectory();\r
-                }\r
-            }\r
-        }\r
-    }\r
-    \r
-    \r
-    \r
-    \r
-    /**\r
-     * {@inheritDoc}\r
-     */\r
-    @Override\r
-    public DataStorageManager getStorageManager() {\r
-        return mStorageManager;\r
-    }\r
-    \r
-    \r
-    /**\r
-     * {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void onDirectoryClick(OCFile directory) {\r
-        pushDirname(directory);\r
-        ActionBar actionBar = getSupportActionBar();\r
-        actionBar.setDisplayHomeAsUpEnabled(true);\r
-        \r
-        if (mDualPane) {\r
-            // Resets the FileDetailsFragment on Tablets so that it always displays\r
-            FileDetailFragment fileDetails = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);\r
-            if (fileDetails != null && !fileDetails.isEmpty()) {\r
-                FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();\r
-                transaction.remove(fileDetails);\r
-                transaction.add(R.id.file_details_container, new FileDetailFragment(null, null), FileDetailFragment.FTAG);\r
-                transaction.commit();\r
-            }\r
-        }\r
-    }\r
-    \r
-    \r
-    /**\r
-     * {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void onFileClick(OCFile file) {\r
-        \r
-        // If we are on a large device -> update fragment\r
-        if (mDualPane) {\r
-            // buttons in the details view are problematic when trying to reuse an existing fragment; create always a new one solves some of them, BUT no all; downloads are 'dangerous'\r
-            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();\r
-            transaction.replace(R.id.file_details_container, new FileDetailFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);\r
-            transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);\r
-            transaction.commit();\r
-            \r
-        } else {    // small or medium screen device -> new Activity\r
-            Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);\r
-            showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);\r
-            showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));\r
-            startActivity(showDetailsIntent);\r
-        }\r
-    }\r
-    \r
-    \r
-    /**\r
-     * {@inheritDoc}\r
-     */\r
-    @Override\r
-    public OCFile getInitialDirectory() {\r
-        return mCurrentDir;\r
-    }\r
-    \r
-    \r
-    /**\r
-     * {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void onFileStateChanged() {\r
-        OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);\r
-        if (fileListFragment != null) { \r
-            fileListFragment.listDirectory();\r
-        }\r
-    }\r
-\r
-    \r
-    /**\r
-     * {@inheritDoc}\r
-     */\r
-    @Override\r
-    public FileDownloaderBinder getFileDownloaderBinder() {\r
-        return mDownloaderBinder;\r
-    }\r
-\r
-    \r
-    /**\r
-     * {@inheritDoc}\r
-     */\r
-    @Override\r
-    public FileUploaderBinder getFileUploaderBinder() {\r
-        return mUploaderBinder;\r
-    }\r
-    \r
-    \r
-    /** Defines callbacks for service binding, passed to bindService() */\r
-    private class ListServiceConnection implements ServiceConnection {\r
-\r
-        @Override\r
-        public void onServiceConnected(ComponentName component, IBinder service) {\r
-            if (component.equals(new ComponentName(FileDisplayActivity.this, FileDownloader.class))) {\r
-                Log.d(TAG, "Download service connected");\r
-                mDownloaderBinder = (FileDownloaderBinder) service;\r
-            } else if (component.equals(new ComponentName(FileDisplayActivity.this, FileUploader.class))) {\r
-                Log.d(TAG, "Upload service connected");\r
-                mUploaderBinder = (FileUploaderBinder) service;\r
-            } else {\r
-                return;\r
-            }\r
-            // a new chance to get the mDownloadBinder through getFileDownloadBinder() - THIS IS A MESS\r
-            if (mFileList != null)\r
-                mFileList.listDirectory();\r
-            if (mDualPane) {\r
-                FileDetailFragment fragment = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);\r
-                if (fragment != null)\r
-                    fragment.updateFileDetails(false);\r
-            }\r
-        }\r
-\r
-        @Override\r
-        public void onServiceDisconnected(ComponentName component) {\r
-            if (component.equals(new ComponentName(FileDisplayActivity.this, FileDownloader.class))) {\r
-                Log.d(TAG, "Download service disconnected");\r
-                mDownloaderBinder = null;\r
-            } else if (component.equals(new ComponentName(FileDisplayActivity.this, FileUploader.class))) {\r
-                Log.d(TAG, "Upload service disconnected");\r
-                mUploaderBinder = null;\r
-            }\r
-        }\r
-    };    \r
-\r
-    \r
-    \r
-    /**\r
-     * Launch an intent to request the PIN code to the user before letting him use the app\r
-     */\r
-    private void requestPinCode() {\r
-        boolean pinStart = false;\r
-        SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());\r
-        pinStart = appPrefs.getBoolean("set_pincode", false);\r
-        if (pinStart) {\r
-            Intent i = new Intent(getApplicationContext(), PinCodeActivity.class);\r
-            i.putExtra(PinCodeActivity.EXTRA_ACTIVITY, "FileDisplayActivity");\r
-            startActivity(i);\r
-        }\r
-    }\r
-\r
-\r
-    @Override\r
-    public void onSavedCertificate() {\r
-        startSynchronization();                \r
-    }\r
-\r
-\r
-    @Override\r
-    public void onFailedSavingCertificate() {\r
-        showDialog(DIALOG_CERT_NOT_SAVED);\r
-    }\r
-\r
-\r
-    /**\r
-     * Updates the view associated to the activity after the finish of some operation over files\r
-     * in the current account.\r
-     * \r
-     * @param operation     Removal operation performed.\r
-     * @param result        Result of the removal.\r
-     */\r
-    @Override\r
-    public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {\r
-        if (operation instanceof RemoveFileOperation) {\r
-            onRemoveFileOperationFinish((RemoveFileOperation)operation, result);\r
-                \r
-        } else if (operation instanceof RenameFileOperation) {\r
-            onRenameFileOperationFinish((RenameFileOperation)operation, result);\r
-            \r
-        } else if (operation instanceof SynchronizeFileOperation) {\r
-            onSynchronizeFileOperationFinish((SynchronizeFileOperation)operation, result);\r
-            \r
-        } else if (operation instanceof CreateFolderOperation) {\r
-            onCreateFolderOperationFinish((CreateFolderOperation)operation, result);\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Updates the view associated to the activity after the finish of an operation trying create a new folder\r
-     * \r
-     * @param operation     Creation operation performed.\r
-     * @param result        Result of the creation.\r
-     */\r
-    private void onCreateFolderOperationFinish(CreateFolderOperation operation, RemoteOperationResult result) {\r
-        if (result.isSuccess()) {\r
-            dismissDialog(DIALOG_SHORT_WAIT);\r
-            mFileList.listDirectory();\r
-            \r
-        } else {\r
-            dismissDialog(DIALOG_SHORT_WAIT);\r
-            try {\r
-                Toast msg = Toast.makeText(FileDisplayActivity.this, R.string.create_dir_fail_msg, Toast.LENGTH_LONG); \r
-                msg.show();\r
-                    \r
-            } catch (NotFoundException e) {\r
-                Log.e(TAG, "Error while trying to show fail message " , e);\r
-            }\r
-        }\r
-    }\r
-\r
-\r
-    /**\r
-     * Updates the view associated to the activity after the finish of an operation trying to remove a \r
-     * file. \r
-     * \r
-     * @param operation     Removal operation performed.\r
-     * @param result        Result of the removal.\r
-     */\r
-    private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) {\r
-        dismissDialog(DIALOG_SHORT_WAIT);\r
-        if (result.isSuccess()) {\r
-            Toast msg = Toast.makeText(this, R.string.remove_success_msg, Toast.LENGTH_LONG);\r
-            msg.show();\r
-            OCFile removedFile = operation.getFile();\r
-            if (mDualPane) {\r
-                FileDetailFragment details = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);\r
-                if (details != null && removedFile.equals(details.getDisplayedFile()) ) {\r
-                    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();\r
-                    transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null)); // empty FileDetailFragment\r
-                    transaction.commit();\r
-                }\r
-            }\r
-            if (mStorageManager.getFileById(removedFile.getParentId()).equals(mCurrentDir)) {\r
-                mFileList.listDirectory();\r
-            }\r
-                \r
-        } else {\r
-            Toast msg = Toast.makeText(this, R.string.remove_fail_msg, Toast.LENGTH_LONG); \r
-            msg.show();\r
-            if (result.isSslRecoverableException()) {\r
-                mLastSslUntrustedServerResult = result;\r
-                showDialog(DIALOG_SSL_VALIDATOR); \r
-            }\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Updates the view associated to the activity after the finish of an operation trying to rename a \r
-     * file. \r
-     * \r
-     * @param operation     Renaming operation performed.\r
-     * @param result        Result of the renaming.\r
-     */\r
-    private void onRenameFileOperationFinish(RenameFileOperation operation, RemoteOperationResult result) {\r
-        dismissDialog(DIALOG_SHORT_WAIT);\r
-        OCFile renamedFile = operation.getFile();\r
-        if (result.isSuccess()) {\r
-            if (mDualPane) {\r
-                FileDetailFragment details = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);\r
-                if (details != null && renamedFile.equals(details.getDisplayedFile()) ) {\r
-                    details.updateFileDetails(renamedFile, AccountUtils.getCurrentOwnCloudAccount(this));\r
-                }\r
-            }\r
-            if (mStorageManager.getFileById(renamedFile.getParentId()).equals(mCurrentDir)) {\r
-                mFileList.listDirectory();\r
-            }\r
-            \r
-        } else {\r
-            if (result.getCode().equals(ResultCode.INVALID_LOCAL_FILE_NAME)) {\r
-                Toast msg = Toast.makeText(this, R.string.rename_local_fail_msg, Toast.LENGTH_LONG); \r
-                msg.show();\r
-                // TODO throw again the new rename dialog\r
-            } else {\r
-                Toast msg = Toast.makeText(this, R.string.rename_server_fail_msg, Toast.LENGTH_LONG); \r
-                msg.show();\r
-                if (result.isSslRecoverableException()) {\r
-                    mLastSslUntrustedServerResult = result;\r
-                    showDialog(DIALOG_SSL_VALIDATOR); \r
-                }\r
-            }\r
-        }\r
-    }\r
-\r
-\r
-    private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation, RemoteOperationResult result) {\r
-        dismissDialog(DIALOG_SHORT_WAIT);\r
-        OCFile syncedFile = operation.getLocalFile();\r
-        if (!result.isSuccess()) {\r
-            if (result.getCode() == ResultCode.SYNC_CONFLICT) {\r
-                Intent i = new Intent(this, ConflictsResolveActivity.class);\r
-                i.putExtra(ConflictsResolveActivity.EXTRA_FILE, syncedFile);\r
-                i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));\r
-                startActivity(i);\r
-                \r
-            } else {\r
-                Toast msg = Toast.makeText(this, R.string.sync_file_fail_msg, Toast.LENGTH_LONG); \r
-                msg.show();\r
-            }\r
-            \r
-        } else {\r
-            if (operation.transferWasRequested()) {\r
-                mFileList.listDirectory();\r
-                onTransferStateChanged(syncedFile, true, true);\r
-                \r
-            } else {\r
-                Toast msg = Toast.makeText(this, R.string.sync_file_nothing_to_do_msg, Toast.LENGTH_LONG); \r
-                msg.show();\r
-            }\r
-        }\r
-    }\r
-\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading) {\r
-        /*OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);\r
-        if (fileListFragment != null) { \r
-            fileListFragment.listDirectory();\r
-        }*/\r
-        if (mDualPane) {\r
-            FileDetailFragment details = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);\r
-            if (details != null && file.equals(details.getDisplayedFile()) ) {\r
-                if (downloading || uploading) {\r
-                    details.updateFileDetails(file, AccountUtils.getCurrentOwnCloudAccount(this));\r
-                } else {\r
-                    details.updateFileDetails(downloading || uploading);\r
-                }\r
-            }\r
-        }\r
-    }\r
-\r
-\r
-    \r
-\r
-\r
-}\r
+/* ownCloud Android client application
+ *   Copyright (C) 2011  Bartek Przybylski
+ *   Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License 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.ui.activity;
+
+import java.io.File;
+
+import android.accounts.Account;
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.app.Dialog;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.content.res.Resources.NotFoundException;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.preference.PreferenceManager;
+import android.provider.MediaStore;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.ActionBar.OnNavigationListener;
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.Window;
+import com.owncloud.android.AccountUtils;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountAuthenticator;
+import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.services.FileDownloader;
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
+import com.owncloud.android.files.services.FileObserverService;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
+import com.owncloud.android.operations.CreateFolderOperation;
+import com.owncloud.android.operations.OnRemoteOperationListener;
+import com.owncloud.android.operations.RemoteOperation;
+import com.owncloud.android.operations.RemoteOperationResult;
+import com.owncloud.android.operations.RemoveFileOperation;
+import com.owncloud.android.operations.RenameFileOperation;
+import com.owncloud.android.operations.SynchronizeFileOperation;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.syncadapter.FileSyncService;
+import com.owncloud.android.ui.dialog.EditNameDialog;
+import com.owncloud.android.ui.dialog.SslValidatorDialog;
+import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
+import com.owncloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener;
+import com.owncloud.android.ui.fragment.FileDetailFragment;
+import com.owncloud.android.ui.fragment.FileFragment;
+import com.owncloud.android.ui.fragment.OCFileListFragment;
+import com.owncloud.android.ui.preview.PreviewImageActivity;
+import com.owncloud.android.ui.preview.PreviewImageFragment;
+import com.owncloud.android.ui.preview.PreviewMediaFragment;
+
+/**
+ * Displays, what files the user has available in his ownCloud.
+ * 
+ * @author Bartek Przybylski
+ * @author David A. Velasco
+ */
+
+public class FileDisplayActivity extends SherlockFragmentActivity implements
+    OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNavigationListener, OnSslValidatorListener, OnRemoteOperationListener, EditNameDialogListener {
+    
+    private ArrayAdapter<String> mDirectories;
+    private OCFile mCurrentDir = null;
+    private OCFile mCurrentFile = null;
+
+    private DataStorageManager mStorageManager;
+    private SyncBroadcastReceiver mSyncBroadcastReceiver;
+    private UploadFinishReceiver mUploadFinishReceiver;
+    private DownloadFinishReceiver mDownloadFinishReceiver;
+    private FileDownloaderBinder mDownloaderBinder = null;
+    private FileUploaderBinder mUploaderBinder = null;
+    private ServiceConnection mDownloadConnection = null, mUploadConnection = null;
+    private RemoteOperationResult mLastSslUntrustedServerResult = null;
+    
+    private OCFileListFragment mFileList;
+    
+    private boolean mDualPane;
+    private boolean mBackFromCreatingFirstAccount;
+    
+    private static final int DIALOG_SETUP_ACCOUNT = 0;
+    public static final int DIALOG_SHORT_WAIT = 1;
+    private static final int DIALOG_CHOOSE_UPLOAD_SOURCE = 2;
+    private static final int DIALOG_SSL_VALIDATOR = 3;
+    private static final int DIALOG_CERT_NOT_SAVED = 4;
+    
+    private static final int ACTION_SELECT_CONTENT_FROM_APPS = 1;
+    private static final int ACTION_SELECT_MULTIPLE_FILES = 2;
+    
+    private static final String TAG = "FileDisplayActivity";
+
+    private OCFile mWaitingToPreview;
+    private Handler mHandler;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        Log_OC.d(getClass().toString(), "onCreate() start");
+        super.onCreate(savedInstanceState);
+
+        mHandler = new Handler();
+
+        /// Load of parameters from received intent
+        Account account = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_ACCOUNT);
+        if (account != null && AccountUtils.setCurrentOwnCloudAccount(this, account.name)) {
+            mCurrentDir = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE); 
+        }
+        
+        /// Load of saved instance state: keep this always before initDataFromCurrentAccount()
+        if(savedInstanceState != null) {
+            // TODO - test if savedInstanceState should take precedence over file in the intent ALWAYS (now), NEVER. SOME TIMES
+            mCurrentDir = savedInstanceState.getParcelable(FileDetailFragment.EXTRA_FILE);
+            mWaitingToPreview = (OCFile) savedInstanceState.getParcelable(FileDetailActivity.KEY_WAITING_TO_PREVIEW);
+
+        } else {
+            mWaitingToPreview = null;
+        }
+        
+        if (!AccountUtils.accountsAreSetup(this)) {
+            /// no account available: FORCE ACCOUNT CREATION
+            mStorageManager = null;
+            createFirstAccount();
+            
+        } else {    /// at least an account is available
+            
+            initDataFromCurrentAccount();   // it checks mCurrentDir and mCurrentFile with the current account
+            
+        }
+        
+        mUploadConnection = new ListServiceConnection(); 
+        mDownloadConnection = new ListServiceConnection();
+        bindService(new Intent(this, FileUploader.class), mUploadConnection, Context.BIND_AUTO_CREATE);
+        bindService(new Intent(this, FileDownloader.class), mDownloadConnection, Context.BIND_AUTO_CREATE);
+
+        // PIN CODE request ;  best location is to decide, let's try this first
+        if (getIntent().getAction() != null && getIntent().getAction().equals(Intent.ACTION_MAIN) && savedInstanceState == null) {
+            requestPinCode();
+        }
+
+        // file observer
+        Intent observer_intent = new Intent(this, FileObserverService.class);
+        observer_intent.putExtra(FileObserverService.KEY_FILE_CMD, FileObserverService.CMD_INIT_OBSERVED_LIST);
+        startService(observer_intent);
+        
+            
+        /// USER INTERFACE
+        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+            
+        // Drop-down navigation 
+        mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
+        OCFile currFile = mCurrentDir;
+        while(mStorageManager != null && currFile != null && currFile.getFileName() != OCFile.PATH_SEPARATOR) {
+            mDirectories.add(currFile.getFileName());
+            currFile = mStorageManager.getFileById(currFile.getParentId());
+        }
+        mDirectories.add(OCFile.PATH_SEPARATOR);
+
+        // Inflate and set the layout view
+        setContentView(R.layout.files);    
+        mFileList = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
+        mDualPane = (findViewById(R.id.file_details_container) != null);
+        if (mDualPane) {
+            if (savedInstanceState == null) initFileDetailsInDualPane();
+        } else {
+            // quick patchES to fix problem in turn from landscape to portrait, when a file is selected in the right pane
+            // TODO serious refactorization in activities and fragments providing file browsing and handling 
+            if (mCurrentFile != null) {
+                onFileClick(mCurrentFile);
+                mCurrentFile = null;
+            }
+            Fragment rightPanel = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
+            if (rightPanel != null) {
+                FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+                transaction.remove(rightPanel);
+                transaction.commit();
+            }
+        }
+            
+        // Action bar setup
+        ActionBar actionBar = getSupportActionBar();
+        actionBar.setHomeButtonEnabled(true);   // mandatory since Android ICS, according to the official documentation
+        actionBar.setDisplayHomeAsUpEnabled(mCurrentDir != null && mCurrentDir.getParentId() != 0);
+        actionBar.setDisplayShowTitleEnabled(false);
+        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
+        actionBar.setListNavigationCallbacks(mDirectories, this);
+        setSupportProgressBarIndeterminateVisibility(false);        // always AFTER setContentView(...) ; to workaround bug in its implementation
+        
+        
+        mBackFromCreatingFirstAccount = false;
+        
+        Log_OC.d(getClass().toString(), "onCreate() end");
+    }
+
+    
+    /**
+     * Launches the account creation activity. To use when no ownCloud account is available
+     */
+    private void createFirstAccount() {
+        Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT);
+        intent.putExtra(android.provider.Settings.EXTRA_AUTHORITIES, new String[] { AccountAuthenticator.AUTHORITY });
+        startActivity(intent);  // the new activity won't be created until this.onStart() and this.onResume() are finished;
+    }
+
+    
+    /**
+     *  Load of state dependent of the existence of an ownCloud account
+     */
+    private void initDataFromCurrentAccount() {
+        /// Storage manager initialization - access to local database
+        mStorageManager = new FileDataStorageManager(
+                AccountUtils.getCurrentOwnCloudAccount(this),
+                getContentResolver());
+
+        /// Check if mCurrentDir is a directory
+        if(mCurrentDir != null && !mCurrentDir.isDirectory()) {
+            mCurrentFile = mCurrentDir;
+            mCurrentDir = mStorageManager.getFileById(mCurrentDir.getParentId());
+        }
+        
+        /// Check if mCurrentDir and mCurrentFile are in the current account, and update them
+        if (mCurrentDir != null) {
+            mCurrentDir = mStorageManager.getFileByPath(mCurrentDir.getRemotePath());   // mCurrentDir == null if it is not in the current account
+        }
+        if (mCurrentFile != null) {
+            if (mCurrentFile.fileExists()) {
+                mCurrentFile = mStorageManager.getFileByPath(mCurrentFile.getRemotePath());   // mCurrentFile == null if it is not in the current account
+            }   // else : keep mCurrentFile with the received value; this is currently the case of an upload in progress, when the user presses the status notification in a landscape tablet
+        }
+        
+        /// Default to root if mCurrentDir was not found
+        if (mCurrentDir == null) {
+            mCurrentDir = mStorageManager.getFileByPath("/"); // will be NULL if the database was never synchronized
+        }
+    }
+        
+    
+    private void initFileDetailsInDualPane() {
+        if (mDualPane && getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG) == null) {
+            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+            if (mCurrentFile != null) {
+                if (PreviewMediaFragment.canBePreviewed(mCurrentFile)) {
+                    if (mCurrentFile.isDown()) {
+                        transaction.replace(R.id.file_details_container, new PreviewMediaFragment(mCurrentFile, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
+                    } else {
+                        transaction.replace(R.id.file_details_container, new FileDetailFragment(mCurrentFile, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
+                        mWaitingToPreview = mCurrentFile;
+                    }
+                } else {
+                    transaction.replace(R.id.file_details_container, new FileDetailFragment(mCurrentFile, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
+                }
+                mCurrentFile = null;
+                
+            } else {
+                transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null), FileDetailFragment.FTAG); // empty FileDetailFragment
+            }
+            transaction.commit();
+        }
+    }
+    
+    
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mDownloadConnection != null)
+            unbindService(mDownloadConnection);
+        if (mUploadConnection != null)
+            unbindService(mUploadConnection);
+    }
+
+    
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getSherlock().getMenuInflater();
+            inflater.inflate(R.menu.main_menu, menu);
+            
+            return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        boolean retval = true;
+        switch (item.getItemId()) {
+            case R.id.action_create_dir: {
+                EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.uploader_info_dirname), "", -1, -1, this);
+                dialog.show(getSupportFragmentManager(), "createdirdialog");
+                break;
+            }
+            case R.id.action_sync_account: {
+                startSynchronization();
+                break;
+            }
+            case R.id.action_upload: {
+                showDialog(DIALOG_CHOOSE_UPLOAD_SOURCE);
+                break;
+            }
+            case R.id.action_settings: {
+                Intent settingsIntent = new Intent(this, Preferences.class);
+                startActivity(settingsIntent);
+                break;
+            }
+            case android.R.id.home: {
+                if(mCurrentDir != null && mCurrentDir.getParentId() != 0){
+                    onBackPressed(); 
+                }
+                break;
+            }
+            default:
+                retval = super.onOptionsItemSelected(item);
+        }
+        return retval;
+    }
+
+    private void startSynchronization() {
+        ContentResolver.cancelSync(null, AccountAuthenticator.AUTHORITY);   // cancel the current synchronizations of any ownCloud account
+        Bundle bundle = new Bundle();
+        bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+        ContentResolver.requestSync(
+                AccountUtils.getCurrentOwnCloudAccount(this),
+                AccountAuthenticator.AUTHORITY, bundle);
+    }
+
+
+    @Override
+    public boolean onNavigationItemSelected(int itemPosition, long itemId) {
+        int i = itemPosition;
+        while (i-- != 0) {
+            onBackPressed();
+        }
+        // the next operation triggers a new call to this method, but it's necessary to 
+        // ensure that the name exposed in the action bar is the current directory when the 
+        // user selected it in the navigation list
+        if (itemPosition != 0)
+            getSupportActionBar().setSelectedNavigationItem(0);
+        return true;
+    }
+
+    /**
+     * Called, when the user selected something for uploading
+     */
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        
+        if (requestCode == ACTION_SELECT_CONTENT_FROM_APPS && (resultCode == RESULT_OK || resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)) {
+            requestSimpleUpload(data, resultCode);
+            
+        } else if (requestCode == ACTION_SELECT_MULTIPLE_FILES && (resultCode == RESULT_OK || resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)) {
+            requestMultipleUpload(data, resultCode);
+            
+        }
+    }
+
+    private void requestMultipleUpload(Intent data, int resultCode) {
+        String[] filePaths = data.getStringArrayExtra(UploadFilesActivity.EXTRA_CHOSEN_FILES);
+        if (filePaths != null) {
+            String[] remotePaths = new String[filePaths.length];
+            String remotePathBase = "";
+            for (int j = mDirectories.getCount() - 2; j >= 0; --j) {
+                remotePathBase += OCFile.PATH_SEPARATOR + mDirectories.getItem(j);
+            }
+            if (!remotePathBase.endsWith(OCFile.PATH_SEPARATOR))
+                remotePathBase += OCFile.PATH_SEPARATOR;
+            for (int j = 0; j< remotePaths.length; j++) {
+                remotePaths[j] = remotePathBase + (new File(filePaths[j])).getName();
+            }
+
+            Intent i = new Intent(this, FileUploader.class);
+            i.putExtra(FileUploader.KEY_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
+            i.putExtra(FileUploader.KEY_LOCAL_FILE, filePaths);
+            i.putExtra(FileUploader.KEY_REMOTE_FILE, remotePaths);
+            i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES);
+            if (resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)
+                i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);
+            startService(i);
+            
+        } else {
+            Log_OC.d("FileDisplay", "User clicked on 'Update' with no selection");
+            Toast t = Toast.makeText(this, getString(R.string.filedisplay_no_file_selected), Toast.LENGTH_LONG);
+            t.show();
+            return;
+        }
+    }
+
+
+    private void requestSimpleUpload(Intent data, int resultCode) {
+        String filepath = null;
+        try {
+            Uri selectedImageUri = data.getData();
+
+            String filemanagerstring = selectedImageUri.getPath();
+            String selectedImagePath = getPath(selectedImageUri);
+
+            if (selectedImagePath != null)
+                filepath = selectedImagePath;
+            else
+                filepath = filemanagerstring;
+            
+        } catch (Exception e) {
+            Log_OC.e("FileDisplay", "Unexpected exception when trying to read the result of Intent.ACTION_GET_CONTENT", e);
+            e.printStackTrace();
+            
+        } finally {
+            if (filepath == null) {
+                Log_OC.e("FileDisplay", "Couldnt resolve path to file");
+                Toast t = Toast.makeText(this, getString(R.string.filedisplay_unexpected_bad_get_content), Toast.LENGTH_LONG);
+                t.show();
+                return;
+            }
+        }
+
+        Intent i = new Intent(this, FileUploader.class);
+        i.putExtra(FileUploader.KEY_ACCOUNT,
+                AccountUtils.getCurrentOwnCloudAccount(this));
+        String remotepath = new String();
+        for (int j = mDirectories.getCount() - 2; j >= 0; --j) {
+            remotepath += OCFile.PATH_SEPARATOR + mDirectories.getItem(j);
+        }
+        if (!remotepath.endsWith(OCFile.PATH_SEPARATOR))
+            remotepath += OCFile.PATH_SEPARATOR;
+        remotepath += new File(filepath).getName();
+
+        i.putExtra(FileUploader.KEY_LOCAL_FILE, filepath);
+        i.putExtra(FileUploader.KEY_REMOTE_FILE, remotepath);
+        i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
+        if (resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)
+            i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);
+        startService(i);
+    }
+
+
+    @Override
+    public void onBackPressed() {
+        if (mDirectories.getCount() <= 1) {
+            finish();
+            return;
+        }
+        popDirname();
+        mFileList.onNavigateUp();
+        mCurrentDir = mFileList.getCurrentFile();
+        
+        if (mDualPane) {
+            // Resets the FileDetailsFragment on Tablets so that it always displays
+            Fragment fileFragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
+            if (fileFragment != null && (fileFragment instanceof PreviewMediaFragment || !((FileDetailFragment) fileFragment).isEmpty())) {
+                FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+                transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null), FileDetailFragment.FTAG); // empty FileDetailFragment                
+                transaction.commit();
+            }
+        }
+        
+        if(mCurrentDir.getParentId() == 0){
+            ActionBar actionBar = getSupportActionBar(); 
+            actionBar.setDisplayHomeAsUpEnabled(false);
+        } 
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        // responsibility of restore is preferred in onCreate() before than in onRestoreInstanceState when there are Fragments involved
+        Log_OC.d(getClass().toString(), "onSaveInstanceState() start");
+        super.onSaveInstanceState(outState);
+        outState.putParcelable(FileDetailFragment.EXTRA_FILE, mCurrentDir);
+        if (mDualPane) {
+            FileFragment fragment = (FileFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
+            if (fragment != null) {
+                OCFile file = fragment.getFile();
+                if (file != null) {
+                    outState.putParcelable(FileDetailFragment.EXTRA_FILE, file);
+                }
+            }
+        }
+        outState.putParcelable(FileDetailActivity.KEY_WAITING_TO_PREVIEW, mWaitingToPreview);
+        Log_OC.d(getClass().toString(), "onSaveInstanceState() end");
+    }
+    
+    @Override
+    protected void onResume() {
+        Log_OC.d(getClass().toString(), "onResume() start");
+        super.onResume();
+
+        if (AccountUtils.accountsAreSetup(this)) {
+            
+            if (mStorageManager == null) {
+                // this is necessary for handling the come back to FileDisplayActivity when the first ownCloud account is created 
+                initDataFromCurrentAccount();
+                if (mDualPane) {
+                    initFileDetailsInDualPane();
+                }
+                mBackFromCreatingFirstAccount = true;
+            }
+            
+            // Listen for sync messages
+            IntentFilter syncIntentFilter = new IntentFilter(FileSyncService.SYNC_MESSAGE);
+            mSyncBroadcastReceiver = new SyncBroadcastReceiver();
+            registerReceiver(mSyncBroadcastReceiver, syncIntentFilter);
+            
+            // Listen for upload messages
+            IntentFilter uploadIntentFilter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE);
+            mUploadFinishReceiver = new UploadFinishReceiver();
+            registerReceiver(mUploadFinishReceiver, uploadIntentFilter);
+            
+            // Listen for download messages
+            IntentFilter downloadIntentFilter = new IntentFilter(FileDownloader.DOWNLOAD_ADDED_MESSAGE);
+            downloadIntentFilter.addAction(FileDownloader.DOWNLOAD_FINISH_MESSAGE);
+            mDownloadFinishReceiver = new DownloadFinishReceiver();
+            registerReceiver(mDownloadFinishReceiver, downloadIntentFilter);
+        
+            // List current directory
+            mFileList.listDirectory(mCurrentDir);   // TODO we should find the way to avoid the need of this (maybe it's not necessary yet; to check)
+            
+        } else {
+            
+            mStorageManager = null;     // an invalid object will be there if all the ownCloud accounts are removed
+            showDialog(DIALOG_SETUP_ACCOUNT);
+            
+        }
+        Log_OC.d(getClass().toString(), "onResume() end");
+    }
+
+    
+    @Override
+    protected void onPause() {
+        Log_OC.d(getClass().toString(), "onPause() start");
+        super.onPause();
+        if (mSyncBroadcastReceiver != null) {
+            unregisterReceiver(mSyncBroadcastReceiver);
+            mSyncBroadcastReceiver = null;
+        }
+        if (mUploadFinishReceiver != null) {
+            unregisterReceiver(mUploadFinishReceiver);
+            mUploadFinishReceiver = null;
+        }
+        if (mDownloadFinishReceiver != null) {
+            unregisterReceiver(mDownloadFinishReceiver);
+            mDownloadFinishReceiver = null;
+        }
+        if (!AccountUtils.accountsAreSetup(this)) {
+            dismissDialog(DIALOG_SETUP_ACCOUNT);
+        }
+        
+        Log_OC.d(getClass().toString(), "onPause() end");
+    }
+
+    
+    @Override
+    protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
+        if (id == DIALOG_SSL_VALIDATOR && mLastSslUntrustedServerResult != null) {
+            ((SslValidatorDialog)dialog).updateResult(mLastSslUntrustedServerResult);
+        }
+    }
+
+    
+    @Override
+    protected Dialog onCreateDialog(int id) {
+        Dialog dialog = null;
+        AlertDialog.Builder builder;
+        switch (id) {
+        case DIALOG_SETUP_ACCOUNT: {
+            builder = new AlertDialog.Builder(this);
+            builder.setTitle(R.string.main_tit_accsetup);
+            builder.setMessage(R.string.main_wrn_accsetup);
+            builder.setCancelable(false);
+            builder.setPositiveButton(android.R.string.ok, new OnClickListener() {
+                public void onClick(DialogInterface dialog, int which) {
+                    createFirstAccount();
+                    dialog.dismiss();
+                }
+            });
+            String message = String.format(getString(R.string.common_exit), getString(R.string.app_name));
+            builder.setNegativeButton(message, new OnClickListener() {
+                public void onClick(DialogInterface dialog, int which) {
+                    dialog.dismiss();
+                    finish();
+                }
+            });
+            //builder.setNegativeButton(android.R.string.cancel, this);
+            dialog = builder.create();
+            break;
+        }
+        case DIALOG_SHORT_WAIT: {
+            ProgressDialog working_dialog = new ProgressDialog(this);
+            working_dialog.setMessage(getResources().getString(
+                    R.string.wait_a_moment));
+            working_dialog.setIndeterminate(true);
+            working_dialog.setCancelable(false);
+            dialog = working_dialog;
+            break;
+        }
+        case DIALOG_CHOOSE_UPLOAD_SOURCE: {
+            
+            String[] items = null;
+            
+            String[] allTheItems = { getString(R.string.actionbar_upload_files),
+                                     getString(R.string.actionbar_upload_from_apps),
+                                     getString(R.string.actionbar_failed_instant_upload) };
+            
+            String[] commonItems = { getString(R.string.actionbar_upload_files),
+                                     getString(R.string.actionbar_upload_from_apps) };
+            
+            if (InstantUploadActivity.IS_ENABLED)
+                items = allTheItems;
+            else 
+                items = commonItems;
+            
+            builder = new AlertDialog.Builder(this);
+            builder.setTitle(R.string.actionbar_upload);
+            builder.setItems(items, new DialogInterface.OnClickListener() {
+                public void onClick(DialogInterface dialog, int item) {
+                    if (item == 0) {
+                        // if (!mDualPane) {
+                        Intent action = new Intent(FileDisplayActivity.this, UploadFilesActivity.class);
+                        action.putExtra(UploadFilesActivity.EXTRA_ACCOUNT,
+                                AccountUtils.getCurrentOwnCloudAccount(FileDisplayActivity.this));
+                        startActivityForResult(action, ACTION_SELECT_MULTIPLE_FILES);
+                        // } else {
+                        // TODO create and handle new fragment
+                        // LocalFileListFragment
+                        // }
+                    } else if (item == 1) {
+                        Intent action = new Intent(Intent.ACTION_GET_CONTENT);
+                        action = action.setType("*/*").addCategory(Intent.CATEGORY_OPENABLE);
+                        startActivityForResult(Intent.createChooser(action, getString(R.string.upload_chooser_title)),
+                                ACTION_SELECT_CONTENT_FROM_APPS);
+                    } else if (item == 2 && InstantUploadActivity.IS_ENABLED) {
+                        Account account = AccountUtils.getCurrentOwnCloudAccount(FileDisplayActivity.this);
+                        Intent action = new Intent(FileDisplayActivity.this, InstantUploadActivity.class);
+                        action.putExtra(FileUploader.KEY_ACCOUNT, account);
+                        startActivity(action);
+                    }
+                }
+            });
+            dialog = builder.create();
+            break;
+        }
+        case DIALOG_SSL_VALIDATOR: {
+            dialog = SslValidatorDialog.newInstance(this, mLastSslUntrustedServerResult, this);
+            break;
+        }
+        case DIALOG_CERT_NOT_SAVED: {
+            builder = new AlertDialog.Builder(this);
+            builder.setMessage(getResources().getString(R.string.ssl_validator_not_saved));
+            builder.setCancelable(false);
+            builder.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        dialog.dismiss();
+                    };
+                });
+            dialog = builder.create();
+            break;
+        }
+        default:
+            dialog = null;
+        }
+    
+        return dialog;
+    }
+
+    
+    /**
+     * Translates a content URI of an image to a physical path
+     * on the disk
+     * @param uri The URI to resolve
+     * @return The path to the image or null if it could not be found
+     */
+    public String getPath(Uri uri) {
+        String[] projection = { MediaStore.Images.Media.DATA };
+        Cursor cursor = managedQuery(uri, projection, null, null, null);
+        if (cursor != null) {
+            int column_index = cursor
+                    .getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
+            cursor.moveToFirst();
+            return cursor.getString(column_index);
+        } 
+        return null;
+    }
+    
+    /**
+     * Pushes a directory to the drop down list
+     * @param directory to push
+     * @throws IllegalArgumentException If the {@link OCFile#isDirectory()} returns false.
+     */
+    public void pushDirname(OCFile directory) {
+        if(!directory.isDirectory()){
+            throw new IllegalArgumentException("Only directories may be pushed!");
+        }
+        mDirectories.insert(directory.getFileName(), 0);
+        mCurrentDir = directory;
+    }
+
+    /**
+     * Pops a directory name from the drop down list
+     * @return True, unless the stack is empty
+     */
+    public boolean popDirname() {
+        mDirectories.remove(mDirectories.getItem(0));
+        return !mDirectories.isEmpty();
+    }
+
+    // Custom array adapter to override text colors
+    private class CustomArrayAdapter<T> extends ArrayAdapter<T> {
+    
+        public CustomArrayAdapter(FileDisplayActivity ctx, int view) {
+            super(ctx, view);
+        }
+    
+        public View getView(int position, View convertView, ViewGroup parent) {
+            View v = super.getView(position, convertView, parent);
+    
+            ((TextView) v).setTextColor(getResources().getColorStateList(
+                    android.R.color.white));
+            return v;
+        }
+    
+        public View getDropDownView(int position, View convertView,
+                ViewGroup parent) {
+            View v = super.getDropDownView(position, convertView, parent);
+    
+            ((TextView) v).setTextColor(getResources().getColorStateList(
+                    android.R.color.white));
+    
+            return v;
+        }
+    
+    }
+
+    private class SyncBroadcastReceiver extends BroadcastReceiver {
+
+        /**
+         * {@link BroadcastReceiver} to enable syncing feedback in UI
+         */
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            boolean inProgress = intent.getBooleanExtra(FileSyncService.IN_PROGRESS, false);
+            String accountName = intent.getStringExtra(FileSyncService.ACCOUNT_NAME);
+
+            Log_OC.d("FileDisplay", "sync of account " + accountName + " is in_progress: " + inProgress);
+
+            if (accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name)) {  
+            
+                String synchFolderRemotePath = intent.getStringExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH); 
+                 
+                boolean fillBlankRoot = false;
+                if (mCurrentDir == null) {
+                    mCurrentDir = mStorageManager.getFileByPath("/");
+                    fillBlankRoot = (mCurrentDir != null);
+                }
+
+                if ((synchFolderRemotePath != null && mCurrentDir != null && (mCurrentDir.getRemotePath().equals(synchFolderRemotePath)))
+                        || fillBlankRoot ) {
+                    if (!fillBlankRoot) 
+                        mCurrentDir = getStorageManager().getFileByPath(synchFolderRemotePath);
+                    OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager()
+                            .findFragmentById(R.id.fileList);
+                    if (fileListFragment != null) {
+                        fileListFragment.listDirectory(mCurrentDir);
+                    }
+                }
+                
+                setSupportProgressBarIndeterminateVisibility(inProgress);
+                if (mBackFromCreatingFirstAccount) {
+                    // awful patch to fix problem with visibility of progress circle with the first refresh of the first account
+                    // TODO - kill this Activity when the first account has to be created instead of stack the account creation on it
+                    getSupportActionBar().hide();
+                    getSupportActionBar().show();
+                    mBackFromCreatingFirstAccount = false;
+                }
+                removeStickyBroadcast(intent);
+                
+            }
+            
+            RemoteOperationResult synchResult = (RemoteOperationResult)intent.getSerializableExtra(FileSyncService.SYNC_RESULT);
+            if (synchResult != null) {
+                if (synchResult.getCode().equals(RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED)) {
+                    mLastSslUntrustedServerResult = synchResult;
+                    showDialog(DIALOG_SSL_VALIDATOR); 
+                }
+            }
+        }
+    }
+    
+
+    private class UploadFinishReceiver extends BroadcastReceiver {
+        /**
+         * Once the file upload has finished -> update view
+         *  @author David A. Velasco
+         * {@link BroadcastReceiver} to enable upload feedback in UI
+         */
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String uploadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
+            String accountName = intent.getStringExtra(FileUploader.ACCOUNT_NAME);
+            boolean sameAccount = accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name);
+            boolean isDescendant = (mCurrentDir != null) && (uploadedRemotePath != null) && (uploadedRemotePath.startsWith(mCurrentDir.getRemotePath()));
+            if (sameAccount && isDescendant) {
+                OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
+                if (fileListFragment != null) { 
+                    fileListFragment.listDirectory();
+                }
+            }
+        }
+        
+    }
+    
+    
+    /**
+     * Class waiting for broadcast events from the {@link FielDownloader} service.
+     * 
+     * Updates the UI when a download is started or finished, provided that it is relevant for the
+     * current folder.
+     */
+    private class DownloadFinishReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            boolean sameAccount = isSameAccount(context, intent);
+            String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
+            boolean isDescendant = isDescendant(downloadedRemotePath);
+            
+            if (sameAccount && isDescendant) {
+                updateLeftPanel();
+                if (mDualPane) {
+                    updateRightPanel(intent.getAction(), downloadedRemotePath, intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false));
+                }
+            }
+            
+            removeStickyBroadcast(intent);
+        }
+
+        private boolean isDescendant(String downloadedRemotePath) {
+            return (mCurrentDir != null && downloadedRemotePath != null && downloadedRemotePath.startsWith(mCurrentDir.getRemotePath()));
+        }
+
+        private boolean isSameAccount(Context context, Intent intent) {
+            String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME);
+            return (accountName != null && accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name));
+        }
+    }
+    
+    
+    protected void updateLeftPanel() {
+        OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
+        if (fileListFragment != null) { 
+            fileListFragment.listDirectory();
+        }
+    }
+
+    protected void updateRightPanel(String downloadEvent, String downloadedRemotePath, boolean success) {
+        Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
+        boolean waitedPreview = (mWaitingToPreview != null && mWaitingToPreview.getRemotePath().equals(downloadedRemotePath));
+        if (fragment != null && fragment instanceof FileDetailFragment) {
+            FileDetailFragment detailsFragment = (FileDetailFragment) fragment;
+            OCFile fileInFragment = detailsFragment.getFile();
+            if (fileInFragment != null && !downloadedRemotePath.equals(fileInFragment.getRemotePath())) {
+                // the user browsed to other file ; forget the automatic preview 
+                mWaitingToPreview = null;
+                
+            } else if (downloadEvent.equals(FileDownloader.DOWNLOAD_ADDED_MESSAGE)) {
+                // grant that the right panel updates the progress bar
+                detailsFragment.listenForTransferProgress();
+                detailsFragment.updateFileDetails(true, false);
+                
+            } else if (downloadEvent.equals(FileDownloader.DOWNLOAD_FINISH_MESSAGE)) {
+                //  update the right panel 
+                if (success && waitedPreview) {
+                    mWaitingToPreview = mStorageManager.getFileById(mWaitingToPreview.getFileId());   // update the file from database, for the local storage path
+                    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+                    transaction.replace(R.id.file_details_container, new PreviewMediaFragment(mWaitingToPreview, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
+                    transaction.commit();
+                    mWaitingToPreview = null;
+                    
+                } else {
+                    detailsFragment.updateFileDetails(false, (success));
+                }
+            }
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public DataStorageManager getStorageManager() {
+        return mStorageManager;
+    }
+    
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onDirectoryClick(OCFile directory) {
+        pushDirname(directory);
+        ActionBar actionBar = getSupportActionBar();
+        actionBar.setDisplayHomeAsUpEnabled(true);
+        
+        if (mDualPane) {
+            // Resets the FileDetailsFragment on Tablets so that it always displays
+            Fragment fileFragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
+            if (fileFragment != null && (fileFragment instanceof PreviewMediaFragment || !((FileDetailFragment) fileFragment).isEmpty())) {
+                FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+                transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null), FileDetailFragment.FTAG); // empty FileDetailFragment                
+                transaction.commit();
+            }
+        }
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onFileClick(OCFile file) {
+        if (file != null && PreviewImageFragment.canBePreviewed(file)) {
+            // preview image - it handles the download, if needed
+            startPreviewImage(file);
+            
+        } else if (file != null && PreviewMediaFragment.canBePreviewed(file)) {
+            if (file.isDown()) {
+                // general preview
+                startMediaPreview(file);
+                
+            } else {
+                // automatic download, preview on finish
+                startDownloadForPreview(file);
+                
+            }
+        } else {
+            // details view
+            startDetails(file);
+        }
+    }
+
+    private void startPreviewImage(OCFile file) {
+        Intent showDetailsIntent = new Intent(this, PreviewImageActivity.class);
+        showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
+        showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
+        startActivity(showDetailsIntent);
+    }
+    
+    private void startMediaPreview(OCFile file) {
+        if (mDualPane) {
+            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+            transaction.replace(R.id.file_details_container, new PreviewMediaFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
+            transaction.commit();
+            
+        } else {
+            Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
+            showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
+            showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
+            startActivity(showDetailsIntent);
+        }
+    }
+    
+    private void startDownloadForPreview(OCFile file) {
+        if (mDualPane) {
+            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+            transaction.replace(R.id.file_details_container, new FileDetailFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
+            transaction.commit();
+            mWaitingToPreview = file;
+            requestForDownload();
+            
+        } else {
+            Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
+            showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
+            showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
+            startActivity(showDetailsIntent);
+        }
+    }
+
+    
+    private void startDetails(OCFile file) {
+        if (mDualPane && !file.isImage()) {
+            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+            transaction.replace(R.id.file_details_container, new FileDetailFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
+            transaction.commit();
+        } else {
+            Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
+            showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
+            showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
+            startActivity(showDetailsIntent);
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public OCFile getInitialDirectory() {
+        return mCurrentDir;
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onFileStateChanged() {
+        OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
+        if (fileListFragment != null) { 
+            fileListFragment.listDirectory();
+        }
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public FileDownloaderBinder getFileDownloaderBinder() {
+        return mDownloaderBinder;
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public FileUploaderBinder getFileUploaderBinder() {
+        return mUploaderBinder;
+    }
+    
+    
+    /** Defines callbacks for service binding, passed to bindService() */
+    private class ListServiceConnection implements ServiceConnection {
+
+        @Override
+        public void onServiceConnected(ComponentName component, IBinder service) {
+            if (component.equals(new ComponentName(FileDisplayActivity.this, FileDownloader.class))) {
+                Log_OC.d(TAG, "Download service connected");
+                mDownloaderBinder = (FileDownloaderBinder) service;
+                if (mWaitingToPreview != null) {
+                    requestForDownload();
+                }
+                
+            } else if (component.equals(new ComponentName(FileDisplayActivity.this, FileUploader.class))) {
+                Log_OC.d(TAG, "Upload service connected");
+                mUploaderBinder = (FileUploaderBinder) service;
+            } else {
+                return;
+            }
+            // a new chance to get the mDownloadBinder through getFileDownloadBinder() - THIS IS A MESS
+            if (mFileList != null)
+                mFileList.listDirectory();
+            if (mDualPane) {
+                Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
+                if (fragment != null && fragment instanceof FileDetailFragment) {
+                    FileDetailFragment detailFragment = (FileDetailFragment)fragment;
+                    detailFragment.listenForTransferProgress();
+                    detailFragment.updateFileDetails(false, false);
+                }
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName component) {
+            if (component.equals(new ComponentName(FileDisplayActivity.this, FileDownloader.class))) {
+                Log_OC.d(TAG, "Download service disconnected");
+                mDownloaderBinder = null;
+            } else if (component.equals(new ComponentName(FileDisplayActivity.this, FileUploader.class))) {
+                Log_OC.d(TAG, "Upload service disconnected");
+                mUploaderBinder = null;
+            }
+        }
+    };    
+
+    
+    
+    /**
+     * Launch an intent to request the PIN code to the user before letting him use the app
+     */
+    private void requestPinCode() {
+        boolean pinStart = false;
+        SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+        pinStart = appPrefs.getBoolean("set_pincode", false);
+        if (pinStart) {
+            Intent i = new Intent(getApplicationContext(), PinCodeActivity.class);
+            i.putExtra(PinCodeActivity.EXTRA_ACTIVITY, "FileDisplayActivity");
+            startActivity(i);
+        }
+    }
+
+
+    @Override
+    public void onSavedCertificate() {
+        startSynchronization();                
+    }
+
+
+    @Override
+    public void onFailedSavingCertificate() {
+        showDialog(DIALOG_CERT_NOT_SAVED);
+    }
+
+
+    /**
+     * Updates the view associated to the activity after the finish of some operation over files
+     * in the current account.
+     * 
+     * @param operation     Removal operation performed.
+     * @param result        Result of the removal.
+     */
+    @Override
+    public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
+        if (operation instanceof RemoveFileOperation) {
+            onRemoveFileOperationFinish((RemoveFileOperation)operation, result);
+                
+        } else if (operation instanceof RenameFileOperation) {
+            onRenameFileOperationFinish((RenameFileOperation)operation, result);
+            
+        } else if (operation instanceof SynchronizeFileOperation) {
+            onSynchronizeFileOperationFinish((SynchronizeFileOperation)operation, result);
+
+        } else if (operation instanceof CreateFolderOperation) {
+            onCreateFolderOperationFinish((CreateFolderOperation)operation, result);
+        }
+    }
+
+
+    /**
+     * Updates the view associated to the activity after the finish of an operation trying to remove a 
+     * file. 
+     * 
+     * @param operation     Removal operation performed.
+     * @param result        Result of the removal.
+     */
+    private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) {
+        dismissDialog(DIALOG_SHORT_WAIT);
+        if (result.isSuccess()) {
+            Toast msg = Toast.makeText(this, R.string.remove_success_msg, Toast.LENGTH_LONG);
+            msg.show();
+            OCFile removedFile = operation.getFile();
+            if (mDualPane) {
+                FileFragment details = (FileFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
+                if (details != null && removedFile.equals(details.getFile())) {
+                    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+                    transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null)); // empty FileDetailFragment
+                    transaction.commit();
+                }
+            }
+            if (mStorageManager.getFileById(removedFile.getParentId()).equals(mCurrentDir)) {
+                mFileList.listDirectory();
+            }
+                
+        } else {
+            Toast msg = Toast.makeText(this, R.string.remove_fail_msg, Toast.LENGTH_LONG); 
+            msg.show();
+            if (result.isSslRecoverableException()) {
+                mLastSslUntrustedServerResult = result;
+                showDialog(DIALOG_SSL_VALIDATOR); 
+            }
+        }
+    }
+
+    /**
+     * Updates the view associated to the activity after the finish of an operation trying create a new folder
+     * 
+     * @param operation     Creation operation performed.
+     * @param result        Result of the creation.
+     */
+    private void onCreateFolderOperationFinish(CreateFolderOperation operation, RemoteOperationResult result) {
+        if (result.isSuccess()) {
+            dismissDialog(DIALOG_SHORT_WAIT);
+            mFileList.listDirectory();
+            
+        } else {
+            dismissDialog(DIALOG_SHORT_WAIT);
+            try {
+                Toast msg = Toast.makeText(FileDisplayActivity.this, R.string.create_dir_fail_msg, 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 a 
+     * file. 
+     * 
+     * @param operation     Renaming operation performed.
+     * @param result        Result of the renaming.
+     */
+    private void onRenameFileOperationFinish(RenameFileOperation operation, RemoteOperationResult result) {
+        dismissDialog(DIALOG_SHORT_WAIT);
+        OCFile renamedFile = operation.getFile();
+        if (result.isSuccess()) {
+            if (mDualPane) {
+                FileFragment details = (FileFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
+                if (details != null && details instanceof FileDetailFragment && renamedFile.equals(details.getFile()) ) {
+                    ((FileDetailFragment) details).updateFileDetails(renamedFile, AccountUtils.getCurrentOwnCloudAccount(this));
+                }
+            }
+            if (mStorageManager.getFileById(renamedFile.getParentId()).equals(mCurrentDir)) {
+                mFileList.listDirectory();
+            }
+            
+        } else {
+            if (result.getCode().equals(ResultCode.INVALID_LOCAL_FILE_NAME)) {
+                Toast msg = Toast.makeText(this, R.string.rename_local_fail_msg, Toast.LENGTH_LONG); 
+                msg.show();
+                // TODO throw again the new rename dialog
+            } else {
+                Toast msg = Toast.makeText(this, R.string.rename_server_fail_msg, Toast.LENGTH_LONG); 
+                msg.show();
+                if (result.isSslRecoverableException()) {
+                    mLastSslUntrustedServerResult = result;
+                    showDialog(DIALOG_SSL_VALIDATOR); 
+                }
+            }
+        }
+    }
+
+
+    private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation, RemoteOperationResult result) {
+        dismissDialog(DIALOG_SHORT_WAIT);
+        OCFile syncedFile = operation.getLocalFile();
+        if (!result.isSuccess()) {
+            if (result.getCode() == ResultCode.SYNC_CONFLICT) {
+                Intent i = new Intent(this, ConflictsResolveActivity.class);
+                i.putExtra(ConflictsResolveActivity.EXTRA_FILE, syncedFile);
+                i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
+                startActivity(i);
+                
+            } else {
+                Toast msg = Toast.makeText(this, R.string.sync_file_fail_msg, Toast.LENGTH_LONG); 
+                msg.show();
+            }
+            
+        } else {
+            if (operation.transferWasRequested()) {
+                mFileList.listDirectory();
+                onTransferStateChanged(syncedFile, true, true);
+                
+            } else {
+                Toast msg = Toast.makeText(this, R.string.sync_file_nothing_to_do_msg, Toast.LENGTH_LONG); 
+                msg.show();
+            }
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading) {
+        /*OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
+        if (fileListFragment != null) { 
+            fileListFragment.listDirectory();
+        }*/
+        if (mDualPane) {
+            FileFragment details = (FileFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
+            if (details != null && details instanceof FileDetailFragment && file.equals(details.getFile()) ) {
+                if (downloading || uploading) {
+                    ((FileDetailFragment)details).updateFileDetails(file, AccountUtils.getCurrentOwnCloudAccount(this));
+                } else {
+                    ((FileDetailFragment)details).updateFileDetails(false, true);
+                }
+            }
+        }
+    }
+
+
+    @Override
+    public void showFragmentWithDetails(OCFile file) {
+        if (mDualPane) {
+            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+            transaction.replace(R.id.file_details_container, new FileDetailFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG); 
+            transaction.commit();
+            
+        } else {
+            Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
+            showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
+            showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
+            showDetailsIntent.putExtra(FileDetailActivity.EXTRA_MODE, FileDetailActivity.MODE_DETAILS);
+            startActivity(showDetailsIntent);
+        }
+    }
+
+    public void onDismiss(EditNameDialog dialog) {
+        //dialog.dismiss();
+        if (dialog.getResult()) {
+            String newDirectoryName = dialog.getNewFilename().trim();
+            Log_OC.d(TAG, "'create directory' dialog dismissed with new name " + newDirectoryName);
+            if (newDirectoryName.length() > 0) {
+                String path;
+                if (mCurrentDir == null) {
+                    // this is just a patch; we should ensure that mCurrentDir never is null
+                    if (!mStorageManager.fileExists(OCFile.PATH_SEPARATOR)) {
+                        OCFile file = new OCFile(OCFile.PATH_SEPARATOR);
+                        mStorageManager.saveFile(file);
+                    }
+                    mCurrentDir = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR);
+                }
+                path = FileDisplayActivity.this.mCurrentDir.getRemotePath();
+                
+                // Create directory
+                path += newDirectoryName + OCFile.PATH_SEPARATOR;
+                RemoteOperation operation = new CreateFolderOperation(path, mCurrentDir.getFileId(), mStorageManager);
+                operation.execute(  AccountUtils.getCurrentOwnCloudAccount(FileDisplayActivity.this), 
+                                    FileDisplayActivity.this, 
+                                    FileDisplayActivity.this, 
+                                    mHandler,
+                                    FileDisplayActivity.this);
+                
+                showDialog(DIALOG_SHORT_WAIT);
+            }
+        }
+    }
+    
+    
+    private void requestForDownload() {
+        Account account = AccountUtils.getCurrentOwnCloudAccount(this);
+        if (!mDownloaderBinder.isDownloading(account, mWaitingToPreview)) {
+            Intent i = new Intent(this, FileDownloader.class);
+            i.putExtra(FileDownloader.EXTRA_ACCOUNT, account);
+            i.putExtra(FileDownloader.EXTRA_FILE, mWaitingToPreview);
+            startService(i);
+        }
+    }
+
+    
+}
index 8a46a00..4c433a9 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
diff --git a/src/com/owncloud/android/ui/activity/InstantUploadActivity.java b/src/com/owncloud/android/ui/activity/InstantUploadActivity.java
new file mode 100644 (file)
index 0000000..99b0b83
--- /dev/null
@@ -0,0 +1,475 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License 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.ui.activity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.accounts.Account;
+import android.app.Activity;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.util.SparseArray;
+import android.view.Gravity;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.owncloud.android.AccountUtils;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.db.DbHandler;
+import com.owncloud.android.files.InstantUploadBroadcastReceiver;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.utils.FileStorageUtils;
+
+/**
+ * This Activity is used to display a list with images they could not be
+ * uploaded instantly. The images can be selected for delete or for a try again
+ * upload
+ * 
+ * The entry-point for this activity is the 'Failed upload Notification" and a
+ * sub-menu underneath the 'Upload' menu-item
+ * 
+ * @author andomaex / Matthias Baumann
+ */
+public class InstantUploadActivity extends Activity {
+
+    private static final String LOG_TAG = InstantUploadActivity.class.getSimpleName();
+    private LinearLayout listView;
+    private static final String retry_chexbox_tag = "retry_chexbox_tag";
+    public static final boolean IS_ENABLED = false;
+    private static int MAX_LOAD_IMAGES = 5;
+    private int lastLoadImageIdx = 0;
+
+    private SparseArray<String> fileList = null;
+    CheckBox failed_upload_all_cb;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.failed_upload_files);
+
+        Button delete_all_btn = (Button) findViewById(R.id.failed_upload_delete_all_btn);
+        delete_all_btn.setOnClickListener(getDeleteListner());
+        Button retry_all_btn = (Button) findViewById(R.id.failed_upload_retry_all_btn);
+        retry_all_btn.setOnClickListener(getRetryListner());
+        this.failed_upload_all_cb = (CheckBox) findViewById(R.id.failed_upload_headline_cb);
+        failed_upload_all_cb.setOnCheckedChangeListener(getCheckAllListener());
+        listView = (LinearLayout) findViewById(R.id.failed_upload_scrollviewlayout);
+
+        loadListView(true);
+
+    }
+
+    /**
+     * init the listview with ImageButtons, checkboxes and filename for every
+     * Image that was not successfully uploaded
+     * 
+     * this method is call at Activity creation and on delete one ore more
+     * list-entry an on retry the upload by clicking the ImageButton or by click
+     * to the 'retry all' button
+     * 
+     */
+    private void loadListView(boolean reset) {
+        DbHandler db = new DbHandler(getApplicationContext());
+        Cursor c = db.getFailedFiles();
+
+        if (reset) {
+            fileList = new SparseArray<String>();
+            listView.removeAllViews();
+            lastLoadImageIdx = 0;
+        }
+        if (c != null) {
+            try {
+                c.moveToPosition(lastLoadImageIdx);
+
+                while (c.moveToNext()) {
+
+                    lastLoadImageIdx++;
+                    String imp_path = c.getString(1);
+                    String message = c.getString(4);
+                    fileList.put(lastLoadImageIdx, imp_path);
+                    LinearLayout rowLayout = getHorizontalLinearLayout(lastLoadImageIdx);
+                    rowLayout.addView(getFileCheckbox(lastLoadImageIdx));
+                    rowLayout.addView(getImageButton(imp_path, lastLoadImageIdx));
+                    rowLayout.addView(getFileButton(imp_path, message, lastLoadImageIdx));
+                    listView.addView(rowLayout);
+                    Log_OC.d(LOG_TAG, imp_path + " on idx: " + lastLoadImageIdx);
+                    if (lastLoadImageIdx % MAX_LOAD_IMAGES == 0) {
+                        break;
+                    }
+                }
+                if (lastLoadImageIdx > 0) {
+                    addLoadMoreButton(listView);
+                }
+            } finally {
+                db.close();
+            }
+        }
+    }
+
+    private void addLoadMoreButton(LinearLayout listView) {
+        if (listView != null) {
+            Button loadmoreBtn = null;
+            View oldButton = listView.findViewById(42);
+            if (oldButton != null) {
+                // remove existing button
+                listView.removeView(oldButton);
+                // to add the button at the end
+                loadmoreBtn = (Button) oldButton;
+            } else {
+                // create a new button to add to the scoll view
+                loadmoreBtn = new Button(this);
+                loadmoreBtn.setId(42);
+                loadmoreBtn.setText(getString(R.string.failed_upload_load_more_images));
+                loadmoreBtn.setBackgroundResource(R.color.owncloud_white);
+                loadmoreBtn.setTextSize(12);
+                loadmoreBtn.setOnClickListener(new OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        loadListView(false);
+                    }
+
+                });
+            }
+            listView.addView(loadmoreBtn);
+        }
+    }
+
+    /**
+     * provide a list of CheckBox instances, looked up from parent listview this
+     * list ist used to select/deselect all checkboxes at the list
+     * 
+     * @return List<CheckBox>
+     */
+    private List<CheckBox> getCheckboxList() {
+        List<CheckBox> list = new ArrayList<CheckBox>();
+        for (int i = 0; i < listView.getChildCount(); i++) {
+            Log_OC.d(LOG_TAG, "ListView has Childs: " + listView.getChildCount());
+            View childView = listView.getChildAt(i);
+            if (childView != null && childView instanceof ViewGroup) {
+                View checkboxView = getChildViews((ViewGroup) childView);
+                if (checkboxView != null && checkboxView instanceof CheckBox) {
+                    Log_OC.d(LOG_TAG, "found Child: " + checkboxView.getId() + " " + checkboxView.getClass());
+                    list.add((CheckBox) checkboxView);
+                }
+            }
+        }
+        return list;
+    }
+
+    /**
+     * recursive called method, used from getCheckboxList method
+     * 
+     * @param View
+     * @return View
+     */
+    private View getChildViews(ViewGroup view) {
+        if (view != null) {
+            for (int i = 0; i < view.getChildCount(); i++) {
+                View cb = view.getChildAt(i);
+                if (cb != null && cb instanceof ViewGroup) {
+                    return getChildViews((ViewGroup) cb);
+                } else if (cb instanceof CheckBox) {
+                    return cb;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * create a new OnCheckedChangeListener for the 'check all' checkbox *
+     * 
+     * @return OnCheckedChangeListener to select all checkboxes at the list
+     */
+    private OnCheckedChangeListener getCheckAllListener() {
+        return new OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                List<CheckBox> list = getCheckboxList();
+                for (CheckBox checkbox : list) {
+                    ((CheckBox) checkbox).setChecked(isChecked);
+                }
+            }
+
+        };
+    }
+
+    /**
+     * Button click Listener for the retry button at the headline
+     * 
+     * @return a Listener to perform a retry for all selected images
+     */
+    private OnClickListener getRetryListner() {
+        return new OnClickListener() {
+
+            @Override
+            public void onClick(View v) {
+
+                try {
+
+                    List<CheckBox> list = getCheckboxList();
+                    for (CheckBox checkbox : list) {
+                        boolean to_retry = checkbox.isChecked();
+
+                        Log_OC.d(LOG_TAG, "Checkbox for " + checkbox.getId() + " was checked: " + to_retry);
+                        String img_path = fileList.get(checkbox.getId());
+                        if (to_retry) {
+
+                            final String msg = "Image-Path " + checkbox.getId() + " was checked: " + img_path;
+                            Log_OC.d(LOG_TAG, msg);
+                            startUpload(img_path);
+                        }
+
+                    }
+                } finally {
+                    // refresh the List
+                    listView.removeAllViews();
+                    loadListView(true);
+                    if (failed_upload_all_cb != null) {
+                        failed_upload_all_cb.setChecked(false);
+                    }
+                }
+
+            }
+        };
+    }
+
+    /**
+     * Button click Listener for the delete button at the headline
+     * 
+     * @return a Listener to perform a delete for all selected images
+     */
+    private OnClickListener getDeleteListner() {
+
+        return new OnClickListener() {
+
+            @Override
+            public void onClick(View v) {
+
+                final DbHandler dbh = new DbHandler(getApplicationContext());
+                try {
+                    List<CheckBox> list = getCheckboxList();
+                    for (CheckBox checkbox : list) {
+                        boolean to_be_delete = checkbox.isChecked();
+
+                        Log_OC.d(LOG_TAG, "Checkbox for " + checkbox.getId() + " was checked: " + to_be_delete);
+                        String img_path = fileList.get(checkbox.getId());
+                        Log_OC.d(LOG_TAG, "Image-Path " + checkbox.getId() + " was checked: " + img_path);
+                        if (to_be_delete) {
+                            boolean deleted = dbh.removeIUPendingFile(img_path);
+                            Log_OC.d(LOG_TAG, "removing " + checkbox.getId() + " was : " + deleted);
+
+                        }
+
+                    }
+                } finally {
+                    dbh.close();
+                    // refresh the List
+                    listView.removeAllViews();
+                    loadListView(true);
+                    if (failed_upload_all_cb != null) {
+                        failed_upload_all_cb.setChecked(false);
+                    }
+                }
+
+            }
+        };
+    }
+
+    private LinearLayout getHorizontalLinearLayout(int id) {
+        LinearLayout linearLayout = new LinearLayout(getApplicationContext());
+        linearLayout.setId(id);
+        linearLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
+                LinearLayout.LayoutParams.MATCH_PARENT));
+        linearLayout.setGravity(Gravity.RIGHT);
+        linearLayout.setOrientation(LinearLayout.HORIZONTAL);
+        return linearLayout;
+    }
+
+    private LinearLayout getVerticalLinearLayout() {
+        LinearLayout linearLayout = new LinearLayout(getApplicationContext());
+        linearLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
+                LinearLayout.LayoutParams.MATCH_PARENT));
+        linearLayout.setGravity(Gravity.TOP);
+        linearLayout.setOrientation(LinearLayout.VERTICAL);
+        return linearLayout;
+    }
+
+    private View getFileButton(final String img_path, String message, int id) {
+
+        TextView failureTextView = new TextView(this);
+        failureTextView.setText(getString(R.string.failed_upload_failure_text) + message);
+        failureTextView.setBackgroundResource(R.color.owncloud_white);
+        failureTextView.setTextSize(8);
+        failureTextView.setOnLongClickListener(getOnLongClickListener(message));
+        failureTextView.setPadding(5, 5, 5, 10);
+        TextView retryButton = new TextView(this);
+        retryButton.setId(id);
+        retryButton.setText(img_path);
+        retryButton.setBackgroundResource(R.color.owncloud_white);
+        retryButton.setTextSize(8);
+        retryButton.setOnClickListener(getImageButtonOnClickListener(img_path));
+        retryButton.setOnLongClickListener(getOnLongClickListener(message));
+        retryButton.setPadding(5, 5, 5, 10);
+        LinearLayout verticalLayout = getVerticalLinearLayout();
+        verticalLayout.addView(retryButton);
+        verticalLayout.addView(failureTextView);
+
+        return verticalLayout;
+    }
+
+    private OnLongClickListener getOnLongClickListener(final String message) {
+        return new OnLongClickListener() {
+
+            @Override
+            public boolean onLongClick(View v) {
+                Log_OC.d(LOG_TAG, message);
+                Toast toast = Toast.makeText(InstantUploadActivity.this, getString(R.string.failed_upload_retry_text)
+                        + message, Toast.LENGTH_LONG);
+                toast.show();
+                return true;
+            }
+
+        };
+    }
+
+    private CheckBox getFileCheckbox(int id) {
+        CheckBox retryCB = new CheckBox(this);
+        retryCB.setId(id);
+        retryCB.setBackgroundResource(R.color.owncloud_white);
+        retryCB.setTextSize(8);
+        retryCB.setTag(retry_chexbox_tag);
+        return retryCB;
+    }
+
+    private ImageButton getImageButton(String img_path, int id) {
+        ImageButton imageButton = new ImageButton(this);
+        imageButton.setId(id);
+        imageButton.setClickable(true);
+        imageButton.setOnClickListener(getImageButtonOnClickListener(img_path));
+
+        // scale and add a thumbnail to the imagebutton
+        int base_scale_size = 32;
+        if (img_path != null) {
+            Log_OC.d(LOG_TAG, "add " + img_path + " to Image Button");
+            BitmapFactory.Options options = new BitmapFactory.Options();
+            options.inJustDecodeBounds = true;
+            Bitmap bitmap = BitmapFactory.decodeFile(img_path, options);
+            int width_tpm = options.outWidth, height_tmp = options.outHeight;
+            int scale = 3;
+            while (true) {
+                if (width_tpm / 2 < base_scale_size || height_tmp / 2 < base_scale_size) {
+                    break;
+                }
+                width_tpm /= 2;
+                height_tmp /= 2;
+                scale++;
+            }
+
+            Log_OC.d(LOG_TAG, "scale Imgae with: " + scale);
+            BitmapFactory.Options options2 = new BitmapFactory.Options();
+            options2.inSampleSize = scale;
+            bitmap = BitmapFactory.decodeFile(img_path, options2);
+
+            if (bitmap != null) {
+                Log_OC.d(LOG_TAG, "loaded Bitmap Bytes: " + bitmap.getRowBytes());
+                imageButton.setImageBitmap(bitmap);
+            } else {
+                Log_OC.d(LOG_TAG, "could not load imgage: " + img_path);
+            }
+        }
+        return imageButton;
+    }
+
+    private OnClickListener getImageButtonOnClickListener(final String img_path) {
+        return new OnClickListener() {
+
+            @Override
+            public void onClick(View v) {
+                startUpload(img_path);
+                loadListView(true);
+            }
+
+        };
+    }
+
+    /**
+     * start uploading a file to the INSTANT_UPLOD_DIR
+     * 
+     * @param img_path
+     */
+    private void startUpload(String img_path) {
+        // extract filename
+        String filename = FileStorageUtils.getInstantUploadFilePath(this, img_path);
+        if (canInstantUpload()) {
+            Account account = AccountUtils.getCurrentOwnCloudAccount(InstantUploadActivity.this);
+            // add file again to upload queue
+            DbHandler db = new DbHandler(InstantUploadActivity.this);
+            try {
+                db.updateFileState(img_path, DbHandler.UPLOAD_STATUS_UPLOAD_LATER, null);
+            } finally {
+                db.close();
+            }
+
+            Intent i = new Intent(InstantUploadActivity.this, FileUploader.class);
+            i.putExtra(FileUploader.KEY_ACCOUNT, account);
+            i.putExtra(FileUploader.KEY_LOCAL_FILE, img_path);
+            i.putExtra(FileUploader.KEY_REMOTE_FILE, filename);
+            i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
+            i.putExtra(com.owncloud.android.files.services.FileUploader.KEY_INSTANT_UPLOAD, true);
+
+            final String msg = "try to upload file with name :" + filename;
+            Log_OC.d(LOG_TAG, msg);
+            Toast toast = Toast.makeText(InstantUploadActivity.this, getString(R.string.failed_upload_retry_text)
+                    + filename, Toast.LENGTH_LONG);
+            toast.show();
+
+            startService(i);
+        } else {
+            Toast toast = Toast.makeText(InstantUploadActivity.this,
+                    getString(R.string.failed_upload_retry_do_nothing_text) + filename, Toast.LENGTH_LONG);
+            toast.show();
+        }
+    }
+
+    private boolean canInstantUpload() {
+
+        if (!InstantUploadBroadcastReceiver.isOnline(this)
+                || (InstantUploadBroadcastReceiver.instantUploadViaWiFiOnly(this) && !InstantUploadBroadcastReceiver
+                        .isConnectedViaWiFi(this))) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+}
\ No newline at end of file
index e3937be..8540bb2 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2011 Bartek Przybylski\r
  *\r
  *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
+ *   it under the terms of the GNU General Public License version 2,\r
+ *   as published by the Free Software Foundation.\r
  *\r
  *   This program is distributed in the hope that it will be useful,\r
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
diff --git a/src/com/owncloud/android/ui/activity/LogHistoryActivity.java b/src/com/owncloud/android/ui/activity/LogHistoryActivity.java
new file mode 100644 (file)
index 0000000..0c99fcf
--- /dev/null
@@ -0,0 +1,118 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License 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.ui.activity;
+
+import java.io.File;
+import java.util.ArrayList;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.ListView;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.SherlockPreferenceActivity;
+import com.actionbarsherlock.view.MenuItem;
+import com.owncloud.android.R;
+import com.owncloud.android.ui.adapter.LogListAdapter;
+import com.owncloud.android.utils.FileStorageUtils;
+
+
+
+public class LogHistoryActivity extends SherlockPreferenceActivity implements OnPreferenceChangeListener {
+    String logpath = FileStorageUtils.getLogPath();
+    File logDIR = null;
+    
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        
+        setContentView(R.layout.log_send_file);
+        setTitle("Log History");
+        ActionBar actionBar = getSherlock().getActionBar();
+        actionBar.setDisplayHomeAsUpEnabled(true);
+        ListView listView = (ListView) findViewById(android.R.id.list);
+        Button deleteHistoryButton = (Button) findViewById(R.id.deleteLogHistoryButton);
+        deleteHistoryButton.setOnClickListener(new OnClickListener() {
+            
+            @Override
+            public void onClick(View v) {
+                File dir = new File(logpath);
+                if (dir != null) {
+                    File[] files = dir.listFiles();
+                    if(files!=null) { 
+                        for(File f: files) {
+                                f.delete();
+                        }
+                    }
+                    dir.delete();
+                }
+                Intent intent = new Intent(getBaseContext(), Preferences.class);
+                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+                startActivity(intent);
+            }
+            
+        });
+        
+       
+        if(logpath != null){
+        logDIR = new File(logpath);
+        }
+        
+        if(logDIR != null && logDIR.isDirectory()) {
+            File[] files = logDIR.listFiles();
+          
+            if (files != null && files.length != 0) {
+                ArrayList<String> logfiles_name = new ArrayList<String>();
+                for (File file : files) {
+                    logfiles_name.add(file.getName());
+                }
+                    String[] logFiles2Array = logfiles_name.toArray(new String[logfiles_name.size()]);
+                    LogListAdapter listadapter = new LogListAdapter(this,logFiles2Array);
+                    listView.setAdapter(listadapter);
+            }
+        }
+    }
+
+    
+    @Override
+    public boolean onMenuItemSelected(int featureId, MenuItem item) {
+        super.onMenuItemSelected(featureId, item);
+        Intent intent;
+
+        switch (item.getItemId()) {
+       
+        case android.R.id.home:
+            intent = new Intent(getBaseContext(), Preferences.class);
+            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+            startActivity(intent);
+            break;
+        default:
+            return false;
+        }
+        return true;
+    }
+    @Override
+    public boolean onPreferenceChange(Preference arg0, Object arg1) {
+        return false;
+    }
+}
\ No newline at end of file
index 4ad0e57..215ea45 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2011 Bartek Przybylski
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index 460c5e0..b49bcb6 100644 (file)
-/* ownCloud Android client application\r
- *   Copyright (C) 2011  Bartek Przybylski\r
- *   Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
- *\r
- *   This program is distributed in the hope that it will be useful,\r
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- *   GNU General Public License for more details.\r
- *\r
- *   You should have received a copy of the GNU General Public License\r
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-package com.owncloud.android.ui.activity;\r
-\r
-import java.util.Vector;\r
-\r
-import android.content.Intent;\r
-import android.content.SharedPreferences;\r
-import android.os.Bundle;\r
-import android.preference.CheckBoxPreference;\r
-import android.preference.ListPreference;\r
-import android.preference.Preference;\r
-import android.preference.PreferenceManager;\r
-import android.preference.Preference.OnPreferenceChangeListener;\r
-import android.preference.Preference.OnPreferenceClickListener;\r
-import android.util.Log;\r
-\r
-import com.actionbarsherlock.app.ActionBar;\r
-import com.actionbarsherlock.app.SherlockPreferenceActivity;\r
-import com.actionbarsherlock.view.Menu;\r
-import com.actionbarsherlock.view.MenuItem;\r
-import com.owncloud.android.OwnCloudSession;\r
-import com.owncloud.android.db.DbHandler;\r
-\r
-import com.owncloud.android.R;\r
-\r
-/**\r
- * An Activity that allows the user to change the application's settings.\r
- * \r
- * @author Bartek Przybylski\r
- * \r
- */\r
-public class Preferences extends SherlockPreferenceActivity implements\r
-        OnPreferenceChangeListener{\r
-    private static final String TAG = "OwnCloudPreferences";\r
-    private final int mNewSession = 47;\r
-    private final int mEditSession = 48;\r
-    private DbHandler mDbHandler;\r
-    private Vector<OwnCloudSession> mSessions;\r
-    //private Account[] mAccounts;\r
-    //private ListPreference mAccountList;\r
-    private ListPreference mTrackingUpdateInterval;\r
-    private CheckBoxPreference mDeviceTracking;\r
-    private CheckBoxPreference pCode;\r
-    private int mSelectedMenuItem;\r
-\r
-    @Override\r
-    public void onCreate(Bundle savedInstanceState) {\r
-        super.onCreate(savedInstanceState);\r
-        mDbHandler = new DbHandler(getBaseContext());\r
-        mSessions = new Vector<OwnCloudSession>();\r
-        addPreferencesFromResource(R.xml.preferences);\r
-        //populateAccountList();\r
-        ActionBar actionBar = getSherlock().getActionBar();\r
-        actionBar.setDisplayHomeAsUpEnabled(true);\r
-        Preference p = findPreference("manage_account");\r
-        if (p != null)\r
-        p.setOnPreferenceClickListener(new OnPreferenceClickListener() {\r
-            @Override\r
-            public boolean onPreferenceClick(Preference preference) {\r
-                Intent i = new Intent(getApplicationContext(), AccountSelectActivity.class);\r
-                startActivity(i);\r
-                return true;\r
-            }\r
-        });\r
-        \r
-        pCode = (CheckBoxPreference) findPreference("set_pincode");\r
-         \r
-        \r
-        if (pCode != null){\r
-            \r
-            pCode.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {\r
-                @Override\r
-                public boolean onPreferenceChange(Preference preference, Object newValue) {\r
-                                          \r
-                    Intent i = new Intent(getApplicationContext(), PinCodeActivity.class);\r
-                    i.putExtra(PinCodeActivity.EXTRA_ACTIVITY, "preferences");\r
-                    i.putExtra(PinCodeActivity.EXTRA_NEW_STATE, newValue.toString());\r
-                    \r
-                    startActivity(i);\r
-                    \r
-                    return true;\r
-                }\r
-            });            \r
-            \r
-        }\r
-        \r
-    }\r
-\r
-\r
-    @Override\r
-    protected void onResume() {\r
-        // TODO Auto-generated method stub\r
-        SharedPreferences appPrefs = PreferenceManager\r
-                .getDefaultSharedPreferences(getApplicationContext());\r
-        \r
-        boolean state = appPrefs.getBoolean("set_pincode", false);\r
-        pCode.setChecked(state);\r
-        \r
-        super.onResume();\r
-    }\r
-\r
-\r
-\r
-    /**\r
-     * Populates the account selector\r
-     *-/\r
-    private void populateAccountList() {\r
-        AccountManager accMan = AccountManager.get(this);\r
-        mAccounts = accMan.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE);\r
-        mAccountList = (ListPreference) findPreference("select_oc_account");\r
-        mAccountList.setOnPreferenceChangeListener(this);\r
-\r
-        // Display the name of the current account if there is any\r
-        Account defaultAccount = AccountUtils.getCurrentOwnCloudAccount(this);\r
-        if (defaultAccount != null) {\r
-            mAccountList.setSummary(defaultAccount.name);\r
-        }\r
-        \r
-        // Transform accounts into array of string for preferences to use\r
-        String[] accNames = new String[mAccounts.length];\r
-        for (int i = 0; i < mAccounts.length; i++) {\r
-            Account account = mAccounts[i];\r
-            accNames[i] = account.name;\r
-        }\r
-\r
-        mAccountList.setEntries(accNames);\r
-        mAccountList.setEntryValues(accNames);\r
-    }*/\r
-\r
-    \r
-    \r
-    @Override\r
-    public boolean onCreateOptionsMenu(Menu menu) {\r
-        super.onCreateOptionsMenu(menu);\r
-        //MenuInflater inflater = getSherlock().getMenuInflater();\r
-        //inflater.inflate(R.menu.prefs_menu, menu);\r
-        return true;\r
-    }\r
-\r
-    @Override\r
-    public boolean onMenuItemSelected(int featureId, MenuItem item) {\r
-        super.onMenuItemSelected(featureId, item);\r
-        Intent intent;\r
-\r
-        switch (item.getItemId()) {\r
-        //case R.id.addSessionItem:\r
-        case 1:\r
-            intent = new Intent(this, PreferencesNewSession.class);\r
-            startActivityForResult(intent, mNewSession);\r
-            break;\r
-        case R.id.SessionContextEdit:\r
-            intent = new Intent(this, PreferencesNewSession.class);\r
-            intent.putExtra("sessionId", mSessions.get(mSelectedMenuItem)\r
-                    .getEntryId());\r
-            intent.putExtra("sessionName", mSessions.get(mSelectedMenuItem)\r
-                    .getName());\r
-            intent.putExtra("sessionURL", mSessions.get(mSelectedMenuItem)\r
-                    .getUrl());\r
-            startActivityForResult(intent, mEditSession);\r
-            break;\r
-        case android.R.id.home:\r
-            intent = new Intent(getBaseContext(), FileDisplayActivity.class);\r
-            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);\r
-            startActivity(intent);\r
-            break;\r
-        default:\r
-            Log.w(TAG, "Unknown menu item triggered");\r
-            return false;\r
-        }\r
-        return true;\r
-    }\r
-\r
-    @Override\r
-    protected void onActivityResult(int requestCode, int resultCode, Intent data) {\r
-        super.onActivityResult(requestCode, resultCode, data);\r
-    }\r
-\r
-    @Override\r
-    protected void onDestroy() {\r
-        mDbHandler.close();\r
-        super.onDestroy();\r
-    }\r
-\r
-    \r
-    \r
-    @Override\r
-    /**\r
-     * Updates various summaries after updates. Also starts and stops \r
-     * the\r
-     */\r
-    public boolean onPreferenceChange(Preference preference, Object newValue) {\r
-        // Update current account summary\r
-        /*if (preference.equals(mAccountList)) {\r
-            mAccountList.setSummary(newValue.toString());\r
-        }\r
-\r
-        // Update tracking interval summary\r
-        else*/ if (preference.equals(mTrackingUpdateInterval)) {\r
-            String trackingSummary = getResources().getString(\r
-                    R.string.prefs_trackmydevice_interval_summary);\r
-            trackingSummary = String.format(trackingSummary,\r
-                    newValue.toString());\r
-            mTrackingUpdateInterval.setSummary(trackingSummary);\r
-        }\r
-\r
-        // Start or stop tracking service\r
-        else if (preference.equals(mDeviceTracking)) {\r
-            Intent locationServiceIntent = new Intent();\r
-            locationServiceIntent\r
-                    .setAction("com.owncloud.android.location.LocationLauncher");\r
-            locationServiceIntent.putExtra("TRACKING_SETTING",\r
-                    (Boolean) newValue);\r
-            sendBroadcast(locationServiceIntent);\r
-        } \r
-        return true;\r
-    }\r
-    \r
-    \r
-\r
-}\r
+/* ownCloud Android client application
+ *   Copyright (C) 2011  Bartek Przybylski
+ *   Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License 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.ui.activity;
+
+import java.io.File;
+import java.util.Vector;
+
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Bundle;
+import android.os.Environment;
+import android.preference.CheckBoxPreference;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.preference.PreferenceManager;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.SherlockPreferenceActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.OwnCloudSession;
+import com.owncloud.android.R;
+import com.owncloud.android.db.DbHandler;
+
+/**
+ * An Activity that allows the user to change the application's settings.
+ * 
+ * @author Bartek Przybylski
+ * 
+ */
+public class Preferences extends SherlockPreferenceActivity implements OnPreferenceChangeListener {
+    
+    private static final String TAG = "OwnCloudPreferences";
+    private final int mNewSession = 47;
+    private final int mEditSession = 48;
+    private DbHandler mDbHandler;
+    private Vector<OwnCloudSession> mSessions;
+    private ListPreference mTrackingUpdateInterval;
+    private CheckBoxPreference mDeviceTracking;
+    private CheckBoxPreference pCode;
+    private CheckBoxPreference pLogging;
+    private Preference pLoggingHistory;
+    private Preference pAboutApp;
+    private int mSelectedMenuItem;
+
+
+    @SuppressWarnings("deprecation")
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mDbHandler = new DbHandler(getBaseContext());
+        mSessions = new Vector<OwnCloudSession>();
+        addPreferencesFromResource(R.xml.preferences);
+        //populateAccountList();
+        ActionBar actionBar = getSherlock().getActionBar();
+        actionBar.setDisplayHomeAsUpEnabled(true);
+        Preference p = findPreference("manage_account");
+        if (p != null)
+        p.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+            @Override
+            public boolean onPreferenceClick(Preference preference) {
+                Intent i = new Intent(getApplicationContext(), AccountSelectActivity.class);
+                startActivity(i);
+                return true;
+            }
+        });
+        
+        pCode = (CheckBoxPreference) findPreference("set_pincode");
+        if (pCode != null){
+            pCode.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
+                @Override
+                public boolean onPreferenceChange(Preference preference, Object newValue) {
+                    Intent i = new Intent(getApplicationContext(), PinCodeActivity.class);
+                    i.putExtra(PinCodeActivity.EXTRA_ACTIVITY, "preferences");
+                    i.putExtra(PinCodeActivity.EXTRA_NEW_STATE, newValue.toString());
+                    startActivity(i);
+                    return true;
+                }
+            });
+            
+           /* About App */
+       pAboutApp = (Preference) findPreference("about_app");
+       if (pAboutApp != null) { 
+               pAboutApp.setTitle(String.format(getString(R.string.about_android), getString(R.string.app_name)));
+               PackageInfo pkg;
+               try {
+                   pkg = getPackageManager().getPackageInfo(getPackageName(), 0);
+                   pAboutApp.setSummary(String.format(getString(R.string.about_version), pkg.versionName));
+               } catch (NameNotFoundException e) {
+                   Log_OC.e(TAG, "Error while showing about dialog", e);
+               }
+       }
+       
+       pLogging = (CheckBoxPreference) findPreference("log_to_file");
+       if (pLogging != null) {
+           pLogging.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
+               @Override
+               public boolean onPreferenceChange(Preference preference, Object newValue) {
+                   
+                   String logpath = Environment.getExternalStorageDirectory()+File.separator+"owncloud"+File.separator+"log";
+                
+                   if(!pLogging.isChecked()) {
+                       Log_OC.d("Debug", "start logging");
+                       Log_OC.v("PATH", logpath);
+                       Log_OC.startLogging(logpath);
+                   }
+                   else {
+                       Log_OC.d("Debug", "stop logging");
+                       Log_OC.stopLogging();
+                   }
+                   return true;
+               }
+           });
+       }
+       
+       pLoggingHistory = (Preference) findPreference("log_history");
+       if (pLoggingHistory != null) {
+           pLoggingHistory.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+            
+            @Override
+            public boolean onPreferenceClick(Preference preference) {
+                Intent intent = new Intent(getApplicationContext(),LogHistoryActivity.class);
+                startActivity(intent);
+                return true;
+            }
+        });
+       }
+      }
+    }
+
+    @Override
+    protected void onResume() {
+        SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+        boolean state = appPrefs.getBoolean("set_pincode", false);
+        pCode.setChecked(state);
+        super.onResume();
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        super.onCreateOptionsMenu(menu);
+        return true;
+    }
+
+    @Override
+    public boolean onMenuItemSelected(int featureId, MenuItem item) {
+        super.onMenuItemSelected(featureId, item);
+        Intent intent;
+
+        switch (item.getItemId()) {
+        //case R.id.addSessionItem:
+        case 1:
+            intent = new Intent(this, PreferencesNewSession.class);
+            startActivityForResult(intent, mNewSession);
+            break;
+        case R.id.SessionContextEdit:
+            intent = new Intent(this, PreferencesNewSession.class);
+            intent.putExtra("sessionId", mSessions.get(mSelectedMenuItem)
+                    .getEntryId());
+            intent.putExtra("sessionName", mSessions.get(mSelectedMenuItem)
+                    .getName());
+            intent.putExtra("sessionURL", mSessions.get(mSelectedMenuItem)
+                    .getUrl());
+            startActivityForResult(intent, mEditSession);
+            break;
+        case android.R.id.home:
+            intent = new Intent(getBaseContext(), FileDisplayActivity.class);
+            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+            startActivity(intent);
+            break;
+        default:
+            Log_OC.w(TAG, "Unknown menu item triggered");
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+    }
+
+    @Override
+    protected void onDestroy() {
+        mDbHandler.close();
+        super.onDestroy();
+    }
+    
+    @Override
+    /**
+     * Updates various summaries after updates. Also starts and stops 
+     * the
+     */
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        // Update current account summary
+        /*if (preference.equals(mAccountList)) {
+            mAccountList.setSummary(newValue.toString());
+        }
+
+        // Update tracking interval summary
+        else*/ if (preference.equals(mTrackingUpdateInterval)) {
+            String trackingSummary = getResources().getString(
+                    R.string.prefs_trackmydevice_interval_summary);
+            trackingSummary = String.format(trackingSummary,
+                    newValue.toString());
+            mTrackingUpdateInterval.setSummary(trackingSummary);
+        }
+
+        // Start or stop tracking service
+        else if (preference.equals(mDeviceTracking)) {
+            Intent locationServiceIntent = new Intent();
+            locationServiceIntent
+                    .setAction("com.owncloud.android.location.LocationLauncher");
+            locationServiceIntent.putExtra("TRACKING_SETTING",
+                    (Boolean) newValue);
+            sendBroadcast(locationServiceIntent);
+        } 
+        return true;
+    }
+}
index 211cce5..c43b29f 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.\r
  *\r
  *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
+ *   it under the terms of the GNU General Public License version 2,\r
+ *   as published by the Free Software Foundation.\r
  *\r
  *   This program is distributed in the hope that it will be useful,\r
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index 0160f7e..fbc348d 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index a6d61d1..16e05f8 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -26,7 +25,6 @@ import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Environment;
 import android.support.v4.app.DialogFragment;
-import android.util.Log;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
@@ -44,6 +42,7 @@ import com.owncloud.android.ui.fragment.LocalFileListFragment;
 import com.owncloud.android.ui.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
 import com.owncloud.android.utils.FileStorageUtils;
 
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.R;
 
 /**
@@ -78,7 +77,7 @@ public class UploadFilesActivity extends SherlockFragmentActivity implements
     
     @Override
     public void onCreate(Bundle savedInstanceState) {
-        Log.d(TAG, "onCreate() start");
+        Log_OC.d(TAG, "onCreate() start");
         super.onCreate(savedInstanceState);
 
         if(savedInstanceState != null) {
@@ -125,7 +124,7 @@ public class UploadFilesActivity extends SherlockFragmentActivity implements
             mCurrentDialog = null;
         }
             
-        Log.d(TAG, "onCreate() end");
+        Log_OC.d(TAG, "onCreate() end");
     }
 
 
@@ -181,10 +180,10 @@ public class UploadFilesActivity extends SherlockFragmentActivity implements
     @Override
     protected void onSaveInstanceState(Bundle outState) {
         // responsibility of restore is preferred in onCreate() before than in onRestoreInstanceState when there are Fragments involved
-        Log.d(TAG, "onSaveInstanceState() start");
+        Log_OC.d(TAG, "onSaveInstanceState() start");
         super.onSaveInstanceState(outState);
         outState.putString(UploadFilesActivity.KEY_DIRECTORY_PATH, mCurrentDir.getAbsolutePath());
-        Log.d(TAG, "onSaveInstanceState() end");
+        Log_OC.d(TAG, "onSaveInstanceState() end");
     }
 
     
@@ -347,15 +346,14 @@ public class UploadFilesActivity extends SherlockFragmentActivity implements
                 String[] args = {getString(R.string.app_name)};
                 ConfirmationDialogFragment dialog = ConfirmationDialogFragment.newInstance(R.string.upload_query_move_foreign_files, args, R.string.common_yes, -1, R.string.common_no);
                 dialog.setOnConfirmationListener(UploadFilesActivity.this);
-                mCurrentDialog = dialog;
-                mCurrentDialog.show(getSupportFragmentManager(), QUERY_TO_MOVE_DIALOG_TAG);
+                dialog.show(getSupportFragmentManager(), QUERY_TO_MOVE_DIALOG_TAG);
             }
         }
     }
 
     @Override
     public void onConfirmation(String callerTag) {
-        Log.d(TAG, "Positive button in dialog was clicked; dialog tag is " + callerTag);
+        Log_OC.d(TAG, "Positive button in dialog was clicked; dialog tag is " + callerTag);
         if (callerTag.equals(QUERY_TO_MOVE_DIALOG_TAG)) {
             // return the list of selected files to the caller activity (success), signaling that they should be moved to the ownCloud folder, instead of copied
             Intent data = new Intent();
@@ -363,25 +361,19 @@ public class UploadFilesActivity extends SherlockFragmentActivity implements
             setResult(RESULT_OK_AND_MOVE, data);
             finish();
         }
-        mCurrentDialog.dismiss();
-        mCurrentDialog = null;
     }
 
 
     @Override
     public void onNeutral(String callerTag) {
-        Log.d(TAG, "Phantom neutral button in dialog was clicked; dialog tag is " + callerTag);
-        mCurrentDialog.dismiss();
-        mCurrentDialog = null;
+        Log_OC.d(TAG, "Phantom neutral button in dialog was clicked; dialog tag is " + callerTag);
     }
 
 
     @Override
     public void onCancel(String callerTag) {
         /// nothing to do; don't finish, let the user change the selection
-        Log.d(TAG, "Negative button in dialog was clicked; dialog tag is " + callerTag);
-        mCurrentDialog.dismiss();
-        mCurrentDialog = null;
+        Log_OC.d(TAG, "Negative button in dialog was clicked; dialog tag is " + callerTag);
     }    
 
     
index ac7aa90..94094ee 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index 8d87993..80f5358 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.\r
  *\r
  *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
+ *   it under the terms of the GNU General Public License version 2,\r
+ *   as published by the Free Software Foundation.\r
  *\r
  *   This program is distributed in the hope that it will be useful,\r
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index 1f588c4..815d3aa 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.\r
  *\r
  *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
+ *   it under the terms of the GNU General Public License version 2,\r
+ *   as published by the Free Software Foundation.\r
  *\r
  *   This program is distributed in the hope that it will be useful,\r
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index d55eb3f..c93a2d9 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
diff --git a/src/com/owncloud/android/ui/adapter/LogListAdapter.java b/src/com/owncloud/android/ui/adapter/LogListAdapter.java
new file mode 100644 (file)
index 0000000..6b6e8f6
--- /dev/null
@@ -0,0 +1,54 @@
+package com.owncloud.android.ui.adapter;
+
+import java.io.File;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Environment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+
+import com.owncloud.android.R;
+
+
+public class LogListAdapter extends ArrayAdapter<String> {
+    private Context context = null;
+    private String[] values;
+    private Uri fileUri = null;
+   
+    
+    public LogListAdapter(Context context, String[] values) {
+        super(context, R.layout.log_item, values);
+        this.context = context;
+        this.values = values;
+    }
+
+    @Override
+    public View getView(final int position, View convertView, ViewGroup parent) {
+        LayoutInflater inflater = (LayoutInflater) context
+                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        View rowView = inflater.inflate(R.layout.log_item, parent, false);
+        TextView listText = (TextView) rowView.findViewById(R.id.log_item_single);
+        listText.setText(values[position]);
+        listText.setTextSize(15);
+        fileUri = Uri.fromFile(new File(Environment.getExternalStorageDirectory()+File.separator+"owncloud"+File.separator+"log"+File.separator+values[position]));
+        listText.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
+                emailIntent.setType("text/rtf");
+                emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, "OwnCloud Logfile");
+                emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, "This is a automatic E-mail send by owncloud/android");
+                emailIntent.putExtra(android.content.Intent.EXTRA_STREAM, fileUri);
+                emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                context.startActivity(Intent.createChooser(emailIntent, "Send mail..."));
+            }
+        });
+        return rowView;
+    }
+}
index 73dc98f..edcd2b3 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index 6a3fa21..6da58bc 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index 069eedb..d45140e 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -26,6 +25,7 @@ import android.os.Bundle;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.WindowManager.LayoutParams;
+import android.widget.EditText;
 import android.widget.TextView;
 
 import com.actionbarsherlock.app.SherlockDialogFragment;
@@ -42,8 +42,10 @@ public class EditNameDialog extends SherlockDialogFragment implements DialogInte
 
     public static final String TAG = EditNameDialog.class.getSimpleName();
     
-    protected static final String ARG_TITLE = "title";
-    protected static final String ARG_NAME = "name";
+    protected static final String ARG_TITLE = "TITLE";
+    protected static final String ARG_NAME = "NAME";
+    protected static final String ARG_SELECTION_START = "SELECTION_START";
+    protected static final String ARG_SELECTION_END = "SELECTION_END";
     
     private String mNewFilename;
     private boolean mResult;
@@ -52,16 +54,20 @@ public class EditNameDialog extends SherlockDialogFragment implements DialogInte
     /**
      * Public factory method to get dialog instances.
      * 
-     * @param title         Text to show as title in the dialog.
-     * @param name          Optional text to include in the text input field when the dialog is shown.
-     * @param listener      Instance to notify when the dialog is dismissed.
+     * @param title             Text to show as title in the dialog.
+     * @param name              Optional text to include in the text input field when the dialog is shown.
+     * @param listener          Instance to notify when the dialog is dismissed.
+     * @param selectionStart    Index to the first character to be selected in the input field; negative value for none
+     * @param selectionEnd      Index to the last character to be selected in the input field; negative value for none
      * @return              New dialog instance, ready to show.
      */
-    static public EditNameDialog newInstance(String title, String name, EditNameDialogListener listener) {
+    static public EditNameDialog newInstance(String title, String name, int selectionStart, int selectionEnd, EditNameDialogListener listener) {
         EditNameDialog f = new EditNameDialog();
         Bundle args = new Bundle();
         args.putString(ARG_TITLE, title);
         args.putString(ARG_NAME, name);
+        args.putInt(ARG_SELECTION_START, selectionStart);
+        args.putInt(ARG_SELECTION_END, selectionEnd);
         f.setArguments(args);
         f.setOnDismissListener(listener);
         return f;
@@ -81,7 +87,7 @@ public class EditNameDialog extends SherlockDialogFragment implements DialogInte
         // Inflate the layout for the dialog
         LayoutInflater inflater = getSherlockActivity().getLayoutInflater();
         View v = inflater.inflate(R.layout.edit_box_dialog, null);  // null parent view because it will go in the dialog layout
-        TextView inputText = ((TextView)v.findViewById(R.id.user_input));
+        EditText inputText = ((EditText)v.findViewById(R.id.user_input));
         inputText.setText(currentName);
         
         // Set it to the dialog 
@@ -99,6 +105,11 @@ public class EditNameDialog extends SherlockDialogFragment implements DialogInte
         Dialog d = builder.create();
 
         inputText.requestFocus();
+        int selectionStart = getArguments().getInt(ARG_SELECTION_START, -1);
+        int selectionEnd = getArguments().getInt(ARG_SELECTION_END, -1);
+        if (selectionStart >= 0 && selectionEnd >= 0) {
+            inputText.setSelection(Math.min(selectionStart, selectionEnd), Math.max(selectionStart, selectionEnd));
+        }
         d.getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE);
         return d;
     }    
index c779623..1dd586d 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -61,7 +60,7 @@ public class IndeterminateProgressDialog extends SherlockDialogFragment {
         dialog.setIndeterminate(true);
         
         /// set message
-        int messageId = getArguments().getInt(ARG_MESSAGE_ID, R.string.text_placeholder);
+        int messageId = getArguments().getInt(ARG_MESSAGE_ID, R.string.placeholder_sentence);
         dialog.setMessage(getString(messageId));
         
         /// set cancellation behavior
index 2143e9a..a5766ee 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -32,12 +31,12 @@ import javax.security.auth.x500.X500Principal;
 import android.app.Dialog;
 import android.content.Context;
 import android.os.Bundle;
-import android.util.Log;
 import android.view.View;
 import android.view.Window;
 import android.widget.Button;
 import android.widget.TextView;
 
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.R;
 import com.owncloud.android.network.CertificateCombinedException;
 import com.owncloud.android.network.OwnCloudClientUtils;
@@ -64,7 +63,7 @@ public class SslValidatorDialog extends Dialog {
      * @param context       Android context where the dialog will live.
      * @param result        Result of a failed remote operation.
      * @param listener      Object to notice when the server certificate was added to the local certificates store.
-     * @return              A new SslValidatorDialog instance, or NULL if the operation can not be recovered
+     * @return              A new SslValidatorDialog instance. NULL if the operation can not be recovered
      *                      by setting the certificate as reliable.
      */
     public static SslValidatorDialog newInstance(Context context, RemoteOperationResult result, OnSslValidatorListener listener) {
@@ -110,13 +109,13 @@ public class SslValidatorDialog extends Dialog {
                             if (mListener != null)
                                 mListener.onSavedCertificate();
                             else
-                                Log.d(TAG, "Nobody there to notify the certificate was saved");
+                                Log_OC.d(TAG, "Nobody there to notify the certificate was saved");
                             
                         } catch (Exception e) {
                             dismiss();
                             if (mListener != null)
                                 mListener.onFailedSavingCertificate();
-                            Log.e(TAG, "Server certificate could not be saved in the known servers trust store ", e);
+                            Log_OC.e(TAG, "Server certificate could not be saved in the known servers trust store ", e);
                         }
                     }
                 });
index 265bb1d..f096103 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index 5ddee24..5abf55d 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index e527d31..d528b34 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -23,9 +22,9 @@ import android.app.AlertDialog;
 import android.app.Dialog;
 import android.content.DialogInterface;
 import android.os.Bundle;
-import android.util.Log;
 
 import com.actionbarsherlock.app.SherlockDialogFragment;
+import com.owncloud.android.Log_OC;
 
 public class ConfirmationDialogFragment extends SherlockDialogFragment {
 
@@ -36,6 +35,8 @@ public class ConfirmationDialogFragment extends SherlockDialogFragment {
     public final static String ARG_NEUTRAL_BTN_RES = "neutral_btn_res";
     public final static String ARG_NEGATIVE_BTN_RES = "negative_btn_res";
     
+    public static final String FTAG_CONFIRMATION = "CONFIRMATION_FRAGMENT";
+
     private ConfirmationDialogFragmentListener mListener;
     
     /**
@@ -73,7 +74,7 @@ public class ConfirmationDialogFragment extends SherlockDialogFragment {
         int negBtn = getArguments().getInt(ARG_NEGATIVE_BTN_RES, -1);
         
         if (confirmationTarget == null || resourceId == -1) {
-            Log.wtf(getTag(), "Calling confirmation dialog without resource or arguments");
+            Log_OC.wtf(getTag(), "Calling confirmation dialog without resource or arguments");
             return null;
         }
 
@@ -90,6 +91,7 @@ public class ConfirmationDialogFragment extends SherlockDialogFragment {
                     new DialogInterface.OnClickListener() {
                         public void onClick(DialogInterface dialog, int whichButton) {
                             mListener.onConfirmation(getTag()); 
+                            dialog.dismiss();
                         }
                     });
         if (neuBtn != -1)
@@ -97,6 +99,7 @@ public class ConfirmationDialogFragment extends SherlockDialogFragment {
                     new DialogInterface.OnClickListener() {
                         public void onClick(DialogInterface dialog, int whichButton) {
                             mListener.onNeutral(getTag()); 
+                            dialog.dismiss();
                         }
                     });
         if (negBtn != -1)
@@ -105,6 +108,7 @@ public class ConfirmationDialogFragment extends SherlockDialogFragment {
                         @Override
                         public void onClick(DialogInterface dialog, int which) {
                             mListener.onCancel(getTag());
+                            dialog.dismiss();
                         }
                     });
       return builder.create();
index 4be1fbb..ec20f86 100644 (file)
-/* ownCloud Android client application\r
- *   Copyright (C) 2011  Bartek Przybylski\r
- *   Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
- *\r
- *   This program is distributed in the hope that it will be useful,\r
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- *   GNU General Public License for more details.\r
- *\r
- *   You should have received a copy of the GNU General Public License\r
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-package com.owncloud.android.ui.fragment;\r
-\r
-import java.io.File;\r
-\r
-import org.apache.commons.httpclient.Credentials;\r
-\r
-import android.accounts.Account;\r
-import android.accounts.AccountManager;\r
-import android.annotation.SuppressLint;\r
-import android.app.Activity;\r
-import android.content.ActivityNotFoundException;\r
-import android.content.BroadcastReceiver;\r
-import android.content.Context;\r
-import android.content.Intent;\r
-import android.content.IntentFilter;\r
-import android.graphics.Bitmap;\r
-import android.graphics.BitmapFactory;\r
-import android.graphics.BitmapFactory.Options;\r
-import android.graphics.Point;\r
-import android.net.Uri;\r
-import android.os.AsyncTask;\r
-import android.os.Bundle;\r
-import android.os.Handler;\r
-import android.support.v4.app.DialogFragment;\r
-import android.support.v4.app.FragmentTransaction;\r
-import android.util.Log;\r
-import android.view.Display;\r
-import android.view.LayoutInflater;\r
-import android.view.View;\r
-import android.view.View.OnClickListener;\r
-import android.view.ViewGroup;\r
-import android.webkit.MimeTypeMap;\r
-import android.widget.Button;\r
-import android.widget.CheckBox;\r
-import android.widget.ImageView;\r
-import android.widget.TextView;\r
-import android.widget.Toast;\r
-\r
-import com.actionbarsherlock.app.SherlockFragment;\r
-import com.owncloud.android.DisplayUtils;\r
-import com.owncloud.android.authentication.AccountAuthenticator;\r
-import com.owncloud.android.datamodel.FileDataStorageManager;\r
-import com.owncloud.android.datamodel.OCFile;\r
-import com.owncloud.android.files.services.FileDownloader;\r
-import com.owncloud.android.files.services.FileObserverService;\r
-import com.owncloud.android.files.services.FileUploader;\r
-import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;\r
-import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;\r
-import com.owncloud.android.network.BearerCredentials;\r
-import com.owncloud.android.operations.OnRemoteOperationListener;\r
-import com.owncloud.android.operations.RemoteOperation;\r
-import com.owncloud.android.operations.RemoteOperationResult;\r
-import com.owncloud.android.operations.RemoteOperationResult.ResultCode;\r
-import com.owncloud.android.operations.RemoveFileOperation;\r
-import com.owncloud.android.operations.RenameFileOperation;\r
-import com.owncloud.android.operations.SynchronizeFileOperation;\r
-import com.owncloud.android.ui.activity.ConflictsResolveActivity;\r
-import com.owncloud.android.ui.activity.FileDetailActivity;\r
-import com.owncloud.android.ui.activity.FileDisplayActivity;\r
-import com.owncloud.android.ui.activity.TransferServiceGetter;\r
-import com.owncloud.android.ui.dialog.EditNameDialog;\r
-import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;\r
-\r
-import com.owncloud.android.R;\r
-import eu.alefzero.webdav.WebdavUtils;\r
-\r
-/**\r
- * This Fragment is used to display the details about a file.\r
- * \r
- * @author Bartek Przybylski\r
- * \r
- */\r
-public class FileDetailFragment extends SherlockFragment implements\r
-        OnClickListener, ConfirmationDialogFragment.ConfirmationDialogFragmentListener, OnRemoteOperationListener, EditNameDialogListener {\r
-\r
-    public static final String EXTRA_FILE = "FILE";\r
-    public static final String EXTRA_ACCOUNT = "ACCOUNT";\r
-\r
-    private FileDetailFragment.ContainerActivity mContainerActivity;\r
-    \r
-    private int mLayout;\r
-    private View mView;\r
-    private OCFile mFile;\r
-    private Account mAccount;\r
-    private FileDataStorageManager mStorageManager;\r
-    private ImageView mPreview;\r
-    \r
-    private DownloadFinishReceiver mDownloadFinishReceiver;\r
-    private UploadFinishReceiver mUploadFinishReceiver;\r
-    \r
-    private Handler mHandler;\r
-    private RemoteOperation mLastRemoteOperation;\r
-    private DialogFragment mCurrentDialog;\r
-\r
-    private static final String TAG = FileDetailFragment.class.getSimpleName();\r
-    public static final String FTAG = "FileDetails"; \r
-    public static final String FTAG_CONFIRMATION = "REMOVE_CONFIRMATION_FRAGMENT";\r
-\r
-    \r
-    /**\r
-     * Creates an empty details fragment.\r
-     * \r
-     * It's necessary to keep a public constructor without parameters; the system uses it when tries to reinstantiate a fragment automatically. \r
-     */\r
-    public FileDetailFragment() {\r
-        mFile = null;\r
-        mAccount = null;\r
-        mStorageManager = null;\r
-        mLayout = R.layout.file_details_empty;\r
-    }\r
-    \r
-    \r
-    /**\r
-     * Creates a details fragment.\r
-     * \r
-     * When 'fileToDetail' or 'ocAccount' are null, creates a dummy layout (to use when a file wasn't tapped before).\r
-     * \r
-     * @param fileToDetail      An {@link OCFile} to show in the fragment\r
-     * @param ocAccount         An ownCloud account; needed to start downloads\r
-     */\r
-    public FileDetailFragment(OCFile fileToDetail, Account ocAccount) {\r
-        mFile = fileToDetail;\r
-        mAccount = ocAccount;\r
-        mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment \r
-        mLayout = R.layout.file_details_empty;\r
-    }\r
-    \r
-    \r
-    @Override\r
-    public void onCreate(Bundle savedInstanceState) {\r
-        super.onCreate(savedInstanceState);\r
-        mHandler = new Handler();\r
-    }\r
-    \r
-\r
-    @Override\r
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,\r
-            Bundle savedInstanceState) {\r
-        super.onCreateView(inflater, container, savedInstanceState);\r
-        \r
-        if (savedInstanceState != null) {\r
-            mFile = savedInstanceState.getParcelable(FileDetailFragment.EXTRA_FILE);\r
-            mAccount = savedInstanceState.getParcelable(FileDetailFragment.EXTRA_ACCOUNT);\r
-        }\r
-        \r
-        if(mFile != null && mAccount != null) {\r
-            mLayout = R.layout.file_details_fragment;\r
-        }\r
-        \r
-        View view = null;\r
-        view = inflater.inflate(mLayout, container, false);\r
-        mView = view;\r
-        \r
-        if (mLayout == R.layout.file_details_fragment) {\r
-            mView.findViewById(R.id.fdKeepInSync).setOnClickListener(this);\r
-            mView.findViewById(R.id.fdRenameBtn).setOnClickListener(this);\r
-            mView.findViewById(R.id.fdDownloadBtn).setOnClickListener(this);\r
-            mView.findViewById(R.id.fdOpenBtn).setOnClickListener(this);\r
-            mView.findViewById(R.id.fdRemoveBtn).setOnClickListener(this);\r
-            //mView.findViewById(R.id.fdShareBtn).setOnClickListener(this);\r
-            mPreview = (ImageView)mView.findViewById(R.id.fdPreview);\r
-        }\r
-        \r
-        updateFileDetails(false);\r
-        return view;\r
-    }\r
-    \r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void onAttach(Activity activity) {\r
-        super.onAttach(activity);\r
-        try {\r
-            mContainerActivity = (ContainerActivity) activity;\r
-        } catch (ClassCastException e) {\r
-            throw new ClassCastException(activity.toString() + " must implement " + FileDetailFragment.ContainerActivity.class.getSimpleName());\r
-        }\r
-    }\r
-    \r
-    \r
-    /**\r
-     * {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void onActivityCreated(Bundle savedInstanceState) {\r
-        super.onActivityCreated(savedInstanceState);\r
-        if (mAccount != null) {\r
-            mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());;\r
-        }\r
-    }\r
-        \r
-\r
-    @Override\r
-    public void onSaveInstanceState(Bundle outState) {\r
-        Log.i(getClass().toString(), "onSaveInstanceState() start");\r
-        super.onSaveInstanceState(outState);\r
-        outState.putParcelable(FileDetailFragment.EXTRA_FILE, mFile);\r
-        outState.putParcelable(FileDetailFragment.EXTRA_ACCOUNT, mAccount);\r
-        Log.i(getClass().toString(), "onSaveInstanceState() end");\r
-    }\r
-\r
-    \r
-    @Override\r
-    public void onResume() {\r
-        super.onResume();\r
-        \r
-        mDownloadFinishReceiver = new DownloadFinishReceiver();\r
-        IntentFilter filter = new IntentFilter(\r
-                FileDownloader.DOWNLOAD_FINISH_MESSAGE);\r
-        getActivity().registerReceiver(mDownloadFinishReceiver, filter);\r
-        \r
-        mUploadFinishReceiver = new UploadFinishReceiver();\r
-        filter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE);\r
-        getActivity().registerReceiver(mUploadFinishReceiver, filter);\r
-        \r
-        mPreview = (ImageView)mView.findViewById(R.id.fdPreview);\r
-    }\r
-\r
-    @Override\r
-    public void onPause() {\r
-        super.onPause();\r
-        \r
-        getActivity().unregisterReceiver(mDownloadFinishReceiver);\r
-        mDownloadFinishReceiver = null;\r
-        \r
-        getActivity().unregisterReceiver(mUploadFinishReceiver);\r
-        mUploadFinishReceiver = null;\r
-        \r
-        if (mPreview != null) {\r
-            mPreview = null;\r
-        }\r
-    }\r
-\r
+/* ownCloud Android client application
+ *   Copyright (C) 2011  Bartek Przybylski
+ *   Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License 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.ui.fragment;
+
+import java.io.File;
+import java.lang.ref.WeakReference;
+
+import android.accounts.Account;
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.FragmentTransaction;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.webkit.MimeTypeMap;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.actionbarsherlock.app.SherlockFragment;
+import com.owncloud.android.DisplayUtils;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.services.FileObserverService;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
+import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
+import com.owncloud.android.operations.OnRemoteOperationListener;
+import com.owncloud.android.operations.RemoteOperation;
+import com.owncloud.android.operations.RemoteOperationResult;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.operations.RemoveFileOperation;
+import com.owncloud.android.operations.RenameFileOperation;
+import com.owncloud.android.operations.SynchronizeFileOperation;
+import com.owncloud.android.ui.activity.ConflictsResolveActivity;
+import com.owncloud.android.ui.activity.FileDetailActivity;
+import com.owncloud.android.ui.activity.FileDisplayActivity;
+import com.owncloud.android.ui.dialog.EditNameDialog;
+import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
+
+import com.owncloud.android.R;
+
+import eu.alefzero.webdav.OnDatatransferProgressListener;
+import eu.alefzero.webdav.WebdavUtils;
+
+/**
+ * This Fragment is used to display the details about a file.
+ * 
+ * @author Bartek Przybylski
+ * @author David A. Velasco
+ */
+public class FileDetailFragment extends SherlockFragment implements
+        OnClickListener, 
+        ConfirmationDialogFragment.ConfirmationDialogFragmentListener, OnRemoteOperationListener, EditNameDialogListener,
+        FileFragment {
+
+    public static final String EXTRA_FILE = "FILE";
+    public static final String EXTRA_ACCOUNT = "ACCOUNT";
+
+    private FileFragment.ContainerActivity mContainerActivity;
+    
+    private int mLayout;
+    private View mView;
+    private OCFile mFile;
+    private Account mAccount;
+    private FileDataStorageManager mStorageManager;
+    
+    private UploadFinishReceiver mUploadFinishReceiver;
+    public ProgressListener mProgressListener;
+    
+    private Handler mHandler;
+    private RemoteOperation mLastRemoteOperation;
+    
+    private static final String TAG = FileDetailFragment.class.getSimpleName();
+    public static final String FTAG = "FileDetails"; 
+    public static final String FTAG_CONFIRMATION = "REMOVE_CONFIRMATION_FRAGMENT";
+    
+
+    /**
+     * Creates an empty details fragment.
+     * 
+     * It's necessary to keep a public constructor without parameters; the system uses it when tries to reinstantiate a fragment automatically. 
+     */
+    public FileDetailFragment() {
+        mFile = null;
+        mAccount = null;
+        mStorageManager = null;
+        mLayout = R.layout.file_details_empty;
+        mProgressListener = null;
+    }
+    
+    /**
+     * Creates a details fragment.
+     * 
+     * When 'fileToDetail' or 'ocAccount' are null, creates a dummy layout (to use when a file wasn't tapped before).
+     * 
+     * @param fileToDetail      An {@link OCFile} to show in the fragment
+     * @param ocAccount         An ownCloud account; needed to start downloads
+     */
+    public FileDetailFragment(OCFile fileToDetail, Account ocAccount) {
+        mFile = fileToDetail;
+        mAccount = ocAccount;
+        mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment 
+        mLayout = R.layout.file_details_empty;
+        mProgressListener = null;
+    }
+    
+    
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mHandler = new Handler();
+    }
+    
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        super.onCreateView(inflater, container, savedInstanceState);
+        
+        if (savedInstanceState != null) {
+            mFile = savedInstanceState.getParcelable(FileDetailFragment.EXTRA_FILE);
+            mAccount = savedInstanceState.getParcelable(FileDetailFragment.EXTRA_ACCOUNT);
+        }
+        
+        if(mFile != null && mAccount != null) {
+            mLayout = R.layout.file_details_fragment;
+        }
+        
+        View view = null;
+        view = inflater.inflate(mLayout, container, false);
+        mView = view;
+        
+        if (mLayout == R.layout.file_details_fragment) {
+            mView.findViewById(R.id.fdKeepInSync).setOnClickListener(this);
+            mView.findViewById(R.id.fdRenameBtn).setOnClickListener(this);
+            mView.findViewById(R.id.fdDownloadBtn).setOnClickListener(this);
+            mView.findViewById(R.id.fdOpenBtn).setOnClickListener(this);
+            mView.findViewById(R.id.fdRemoveBtn).setOnClickListener(this);
+            //mView.findViewById(R.id.fdShareBtn).setOnClickListener(this);
+            ProgressBar progressBar = (ProgressBar)mView.findViewById(R.id.fdProgressBar);
+            mProgressListener = new ProgressListener(progressBar);
+        }
+        
+        updateFileDetails(false, false);
+        return view;
+    }
+    
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        try {
+            mContainerActivity = (ContainerActivity) activity;
+            
+        } catch (ClassCastException e) {
+            throw new ClassCastException(activity.toString() + " must implement " + FileDetailFragment.ContainerActivity.class.getSimpleName());
+        }
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        if (mAccount != null) {
+            mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());
+        }
+    }
+        
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putParcelable(FileDetailFragment.EXTRA_FILE, mFile);
+        outState.putParcelable(FileDetailFragment.EXTRA_ACCOUNT, mAccount);
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        listenForTransferProgress();
+    }
+    
+    @Override
+    public void onResume() {
+        super.onResume();
+        mUploadFinishReceiver = new UploadFinishReceiver();
+        IntentFilter filter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE);
+        getActivity().registerReceiver(mUploadFinishReceiver, filter);
+
+    }
+
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        if (mUploadFinishReceiver != null) {
+            getActivity().unregisterReceiver(mUploadFinishReceiver);
+            mUploadFinishReceiver = null;
+        }
+    }
+
+    
+    @Override
+    public void onStop() {
+        super.onStop();
+        leaveTransferProgress();
+    }
+
+    
     @Override\r
     public View getView() {\r
         return super.getView() == null ? mView : super.getView();\r
     }\r
-\r
-    \r
+
     \r
     @Override\r
     public void onClick(View v) {\r
@@ -304,740 +293,714 @@ public class FileDetailFragment extends SherlockFragment implements
                 }\r
                 break;\r
             }\r
-            case R.id.fdKeepInSync: {\r
-                CheckBox cb = (CheckBox) getView().findViewById(R.id.fdKeepInSync);\r
-                mFile.setKeepInSync(cb.isChecked());\r
-                mStorageManager.saveFile(mFile);\r
-                \r
-                /// register the OCFile instance in the observer service to monitor local updates;\r
-                /// if necessary, the file is download \r
-                Intent intent = new Intent(getActivity().getApplicationContext(),\r
-                                           FileObserverService.class);\r
-                intent.putExtra(FileObserverService.KEY_FILE_CMD,\r
-                           (cb.isChecked()?\r
-                                   FileObserverService.CMD_ADD_OBSERVED_FILE:\r
-                                   FileObserverService.CMD_DEL_OBSERVED_FILE));\r
-                intent.putExtra(FileObserverService.KEY_CMD_ARG_FILE, mFile);\r
-                intent.putExtra(FileObserverService.KEY_CMD_ARG_ACCOUNT, mAccount);\r
-                Log.e(TAG, "starting observer service");\r
-                getActivity().startService(intent);\r
-                \r
-                if (mFile.keepInSync()) {\r
-                    onClick(getView().findViewById(R.id.fdDownloadBtn));    // force an immediate synchronization\r
-                }\r
-                break;\r
-            }\r
-            case R.id.fdRenameBtn: {\r
-                EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), mFile.getFileName(), this);\r
-                dialog.show(getFragmentManager(), "nameeditdialog");\r
-                break;\r
-            }   \r
-            case R.id.fdRemoveBtn: {\r
-                ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(\r
-                        R.string.confirmation_remove_alert,\r
-                        new String[]{mFile.getFileName()},\r
-                        mFile.isDown() ? R.string.confirmation_remove_remote_and_local : R.string.confirmation_remove_remote,\r
-                        mFile.isDown() ? R.string.confirmation_remove_local : -1,\r
-                        R.string.common_cancel);\r
-                confDialog.setOnConfirmationListener(this);\r
-                mCurrentDialog = confDialog;\r
-                mCurrentDialog.show(getFragmentManager(), FTAG_CONFIRMATION);\r
-                break;\r
-            }\r
-            case R.id.fdOpenBtn: {\r
-                String storagePath = mFile.getStoragePath();\r
-                String encodedStoragePath = WebdavUtils.encodePath(storagePath);\r
-                try {\r
-                    Intent i = new Intent(Intent.ACTION_VIEW);\r
-                    i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mFile.getMimetype());\r
-                    i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);\r
-                    startActivity(i);\r
-                    \r
-                } catch (Throwable t) {\r
-                    Log.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + mFile.getMimetype());\r
-                    boolean toastIt = true; \r
-                    String mimeType = "";\r
-                    try {\r
-                        Intent i = new Intent(Intent.ACTION_VIEW);\r
-                        mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1));\r
-                        if (mimeType == null || !mimeType.equals(mFile.getMimetype())) {\r
-                            if (mimeType != null) {\r
-                                i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mimeType);\r
-                            } else {\r
-                                // desperate try\r
-                                i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), "*/*");\r
-                            }\r
-                            i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);\r
-                            startActivity(i);\r
-                            toastIt = false;\r
-                        }\r
-                        \r
-                    } catch (IndexOutOfBoundsException e) {\r
-                        Log.e(TAG, "Trying to find out MIME type of a file without extension: " + storagePath);\r
-                        \r
-                    } catch (ActivityNotFoundException e) {\r
-                        Log.e(TAG, "No activity found to handle: " + storagePath + " with MIME type " + mimeType + " obtained from extension");\r
-                        \r
-                    } catch (Throwable th) {\r
-                        Log.e(TAG, "Unexpected problem when opening: " + storagePath, th);\r
-                        \r
-                    } finally {\r
-                        if (toastIt) {\r
-                            Toast.makeText(getActivity(), "There is no application to handle file " + mFile.getFileName(), Toast.LENGTH_SHORT).show();\r
-                        }\r
-                    }\r
-                    \r
-                }\r
-                break;\r
-            }\r
-            default:\r
-                Log.e(TAG, "Incorrect view clicked!");\r
-        }\r
-        \r
-        /* else if (v.getId() == R.id.fdShareBtn) {\r
-            Thread t = new Thread(new ShareRunnable(mFile.getRemotePath()));\r
-            t.start();\r
-        }*/\r
-    }\r
-    \r
-    \r
-    @Override\r
-    public void onConfirmation(String callerTag) {\r
-        if (callerTag.equals(FTAG_CONFIRMATION)) {\r
-            if (mStorageManager.getFileById(mFile.getFileId()) != null) {\r
-                mLastRemoteOperation = new RemoveFileOperation( mFile, \r
-                                                                true, \r
-                                                                mStorageManager);\r
-                mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());\r
-                boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;\r
-                getActivity().showDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);\r
-            }\r
-        }\r
-        mCurrentDialog.dismiss();\r
-        mCurrentDialog = null;\r
-    }\r
-    \r
-    @Override\r
-    public void onNeutral(String callerTag) {\r
-        File f = null;\r
-        if (mFile.isDown() && (f = new File(mFile.getStoragePath())).exists()) {\r
-            f.delete();\r
-            mFile.setStoragePath(null);\r
-            mStorageManager.saveFile(mFile);\r
-            updateFileDetails(mFile, mAccount);\r
-        }\r
-        mCurrentDialog.dismiss();\r
-        mCurrentDialog = null;\r
-    }\r
-    \r
-    @Override\r
-    public void onCancel(String callerTag) {\r
-        Log.d(TAG, "REMOVAL CANCELED");\r
-        mCurrentDialog.dismiss();\r
-        mCurrentDialog = null;\r
-    }\r
-    \r
-    \r
-    /**\r
-     * Check if the fragment was created with an empty layout. An empty fragment can't show file details, must be replaced.\r
-     * \r
-     * @return  True when the fragment was created with the empty layout.\r
-     */\r
-    public boolean isEmpty() {\r
-        return (mLayout == R.layout.file_details_empty || mFile == null || mAccount == null);\r
-    }\r
-\r
-    \r
-    /**\r
-     * Can be used to get the file that is currently being displayed.\r
-     * @return The file on the screen.\r
-     */\r
-    public OCFile getDisplayedFile(){\r
-        return mFile;\r
-    }\r
-    \r
-    /**\r
-     * Use this method to signal this Activity that it shall update its view.\r
-     * \r
-     * @param file : An {@link OCFile}\r
-     */\r
-    public void updateFileDetails(OCFile file, Account ocAccount) {\r
-        mFile = file;\r
-        if (ocAccount != null && ( \r
-                mStorageManager == null || \r
-                (mAccount != null && !mAccount.equals(ocAccount))\r
-           )) {\r
-            mStorageManager = new FileDataStorageManager(ocAccount, getActivity().getApplicationContext().getContentResolver());\r
-        }\r
-        mAccount = ocAccount;\r
-        updateFileDetails(false);\r
-    }\r
-    \r
-\r
-    /**\r
-     * Updates the view with all relevant details about that file.\r
-     *\r
-     * TODO Remove parameter when the transferring state of files is kept in database. \r
-     * \r
-     * @param transferring      Flag signaling if the file should be considered as downloading or uploading, \r
-     *                          although {@link FileDownloaderBinder#isDownloading(Account, OCFile)}  and \r
-     *                          {@link FileUploaderBinder#isUploading(Account, OCFile)} return false.\r
-     * \r
-     */\r
-    public void updateFileDetails(boolean transferring) {\r
-\r
-        if (mFile != null && mAccount != null && mLayout == R.layout.file_details_fragment) {\r
-            \r
-            // set file details\r
-            setFilename(mFile.getFileName());\r
-            setFiletype(mFile.getMimetype());\r
-            setFilesize(mFile.getFileLength());\r
-            if(ocVersionSupportsTimeCreated()){\r
-                setTimeCreated(mFile.getCreationTimestamp());\r
-            }\r
-           \r
-            setTimeModified(mFile.getModificationTimestamp());\r
-            \r
-            CheckBox cb = (CheckBox)getView().findViewById(R.id.fdKeepInSync);\r
-            cb.setChecked(mFile.keepInSync());\r
-\r
-            // configure UI for depending upon local state of the file\r
-            //if (FileDownloader.isDownloading(mAccount, mFile.getRemotePath()) || FileUploader.isUploading(mAccount, mFile.getRemotePath())) {\r
-            FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();\r
-            FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();\r
-            if (transferring || (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile)) || (uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile))) {\r
-                setButtonsForTransferring();\r
-                \r
-            } else if (mFile.isDown()) {\r
-                // Update preview\r
-                if (mFile.getMimetype().startsWith("image/")) {\r
-                    BitmapLoader bl = new BitmapLoader();\r
-                    bl.execute(new String[]{mFile.getStoragePath()});\r
-                }\r
-                \r
-                setButtonsForDown();\r
-                \r
-            } else {\r
-                // TODO load default preview image; when the local file is removed, the preview remains there\r
-                setButtonsForRemote();\r
-            }\r
-        }\r
-        getView().invalidate();\r
-    }\r
-    \r
-    \r
-    /**\r
-     * Updates the filename in view\r
-     * @param filename to set\r
-     */\r
-    private void setFilename(String filename) {\r
-        TextView tv = (TextView) getView().findViewById(R.id.fdFilename);\r
-        if (tv != null)\r
-            tv.setText(filename);\r
-    }\r
-\r
-    /**\r
-     * Updates the MIME type in view\r
-     * @param mimetype to set\r
-     */\r
-    private void setFiletype(String mimetype) {\r
-        TextView tv = (TextView) getView().findViewById(R.id.fdType);\r
-        if (tv != null) {\r
-            String printableMimetype = DisplayUtils.convertMIMEtoPrettyPrint(mimetype);;        \r
-            tv.setText(printableMimetype);\r
-        }\r
-        ImageView iv = (ImageView) getView().findViewById(R.id.fdIcon);\r
-        if (iv != null) {\r
-            iv.setImageResource(DisplayUtils.getResourceId(mimetype));\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Updates the file size in view\r
-     * @param filesize in bytes to set\r
-     */\r
-    private void setFilesize(long filesize) {\r
-        TextView tv = (TextView) getView().findViewById(R.id.fdSize);\r
-        if (tv != null)\r
-            tv.setText(DisplayUtils.bytesToHumanReadable(filesize));\r
-    }\r
-    \r
-    /**\r
-     * Updates the time that the file was created in view\r
-     * @param milliseconds Unix time to set\r
-     */\r
-    private void setTimeCreated(long milliseconds){\r
-        TextView tv = (TextView) getView().findViewById(R.id.fdCreated);\r
-        TextView tvLabel = (TextView) getView().findViewById(R.id.fdCreatedLabel);\r
-        if(tv != null){\r
-            tv.setText(DisplayUtils.unixTimeToHumanReadable(milliseconds));\r
-            tv.setVisibility(View.VISIBLE);\r
-            tvLabel.setVisibility(View.VISIBLE);\r
-        }\r
-    }\r
-    \r
-    /**\r
-     * Updates the time that the file was last modified\r
-     * @param milliseconds Unix time to set\r
-     */\r
-    private void setTimeModified(long milliseconds){\r
-        TextView tv = (TextView) getView().findViewById(R.id.fdModified);\r
-        if(tv != null){\r
-            tv.setText(DisplayUtils.unixTimeToHumanReadable(milliseconds));\r
-        }\r
-    }\r
-    \r
-    /**\r
-     * Enables or disables buttons for a file being downloaded\r
-     */\r
-    private void setButtonsForTransferring() {\r
-        if (!isEmpty()) {\r
-            Button downloadButton = (Button) getView().findViewById(R.id.fdDownloadBtn);\r
-            downloadButton.setText(R.string.common_cancel);\r
-            //downloadButton.setEnabled(false);\r
-        \r
-            // let's protect the user from himself ;)\r
-            ((Button) getView().findViewById(R.id.fdOpenBtn)).setEnabled(false);\r
-            ((Button) getView().findViewById(R.id.fdRenameBtn)).setEnabled(false);\r
-            ((Button) getView().findViewById(R.id.fdRemoveBtn)).setEnabled(false);\r
-            getView().findViewById(R.id.fdKeepInSync).setEnabled(false);\r
-        }\r
-    }\r
-    \r
-    /**\r
-     * Enables or disables buttons for a file locally available \r
-     */\r
-    private void setButtonsForDown() {\r
-        if (!isEmpty()) {\r
-            Button downloadButton = (Button) getView().findViewById(R.id.fdDownloadBtn);\r
-            downloadButton.setText(R.string.filedetails_sync_file);\r
-        \r
-            ((Button) getView().findViewById(R.id.fdOpenBtn)).setEnabled(true);\r
-            ((Button) getView().findViewById(R.id.fdRenameBtn)).setEnabled(true);\r
-            ((Button) getView().findViewById(R.id.fdRemoveBtn)).setEnabled(true);\r
-            getView().findViewById(R.id.fdKeepInSync).setEnabled(true);\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Enables or disables buttons for a file not locally available \r
-     */\r
-    private void setButtonsForRemote() {\r
-        if (!isEmpty()) {\r
-            Button downloadButton = (Button) getView().findViewById(R.id.fdDownloadBtn);\r
-            downloadButton.setText(R.string.filedetails_download);\r
-            \r
-            ((Button) getView().findViewById(R.id.fdOpenBtn)).setEnabled(false);\r
-            ((Button) getView().findViewById(R.id.fdRenameBtn)).setEnabled(true);\r
-            ((Button) getView().findViewById(R.id.fdRemoveBtn)).setEnabled(true);\r
-            getView().findViewById(R.id.fdKeepInSync).setEnabled(true);\r
-        }\r
-    }\r
-    \r
-\r
-    /**\r
-     * In ownCloud 3.X.X and 4.X.X there is a bug that SabreDAV does not return\r
-     * the time that the file was created. There is a chance that this will\r
-     * be fixed in future versions. Use this method to check if this version of\r
-     * ownCloud has this fix.\r
-     * @return True, if ownCloud the ownCloud version is supporting creation time\r
-     */\r
-    private boolean ocVersionSupportsTimeCreated(){\r
-        /*if(mAccount != null){\r
-            AccountManager accManager = (AccountManager) getActivity().getSystemService(Context.ACCOUNT_SERVICE);\r
-            OwnCloudVersion ocVersion = new OwnCloudVersion(accManager\r
-                    .getUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION));\r
-            if(ocVersion.compareTo(new OwnCloudVersion(0x030000)) < 0) {\r
-                return true;\r
-            }\r
-        }*/\r
-        return false;\r
-    }\r
-    \r
-    \r
-    /**\r
-     * Interface to implement by any Activity that includes some instance of FileDetailFragment\r
-     * \r
-     * @author David A. Velasco\r
-     */\r
-    public interface ContainerActivity extends TransferServiceGetter {\r
-\r
-        /**\r
-         * Callback method invoked when the detail fragment wants to notice its container \r
-         * activity about a relevant state the file shown by the fragment.\r
-         * \r
-         * Added to notify to FileDisplayActivity about the need of refresh the files list. \r
-         * \r
-         * Currently called when:\r
-         *  - a download is started;\r
-         *  - a rename is completed;\r
-         *  - a deletion is completed;\r
-         *  - the 'inSync' flag is changed;\r
-         */\r
-        public void onFileStateChanged();\r
-        \r
-    }\r
-    \r
-\r
-    /**\r
-     * Once the file download has finished -> update view\r
-     * @author Bartek Przybylski\r
-     */\r
-    private class DownloadFinishReceiver extends BroadcastReceiver {\r
-        @Override\r
-        public void onReceive(Context context, Intent intent) {\r
-            String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME);\r
-\r
-            if (!isEmpty() && accountName.equals(mAccount.name)) {\r
-                boolean downloadWasFine = intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false);\r
-                String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);\r
-                if (mFile.getRemotePath().equals(downloadedRemotePath)) {\r
-                    if (downloadWasFine) {\r
-                        mFile = mStorageManager.getFileByPath(downloadedRemotePath);\r
-                    }\r
-                    updateFileDetails(false);    // it updates the buttons; must be called although !downloadWasFine\r
-                }\r
-            }\r
-        }\r
-    }\r
-    \r
-    \r
-    /**\r
-     * Once the file upload has finished -> update view\r
-     * \r
-     * Being notified about the finish of an upload is necessary for the next sequence:\r
-     *   1. Upload a big file.\r
-     *   2. Force a synchronization; if it finished before the upload, the file in transfer will be included in the local database and in the file list\r
-     *      of its containing folder; the the server includes it in the PROPFIND requests although it's not fully upload. \r
-     *   3. Click the file in the list to see its details.\r
-     *   4. Wait for the upload finishes; at this moment, the details view must be refreshed to enable the action buttons.\r
-     */\r
-    private class UploadFinishReceiver extends BroadcastReceiver {\r
-        @Override\r
-        public void onReceive(Context context, Intent intent) {\r
-            String accountName = intent.getStringExtra(FileUploader.ACCOUNT_NAME);\r
-\r
-            if (!isEmpty() && accountName.equals(mAccount.name)) {\r
-                boolean uploadWasFine = intent.getBooleanExtra(FileUploader.EXTRA_UPLOAD_RESULT, false);\r
-                String uploadRemotePath = intent.getStringExtra(FileUploader.EXTRA_REMOTE_PATH);\r
-                boolean renamedInUpload = mFile.getRemotePath().equals(intent.getStringExtra(FileUploader.EXTRA_OLD_REMOTE_PATH));\r
-                if (mFile.getRemotePath().equals(uploadRemotePath) ||\r
-                    renamedInUpload) {\r
-                    if (uploadWasFine) {\r
-                       mFile = mStorageManager.getFileByPath(uploadRemotePath);\r
-                    }\r
-                    if (renamedInUpload) {\r
-                        String newName = (new File(uploadRemotePath)).getName();\r
-                        Toast msg = Toast.makeText(getActivity().getApplicationContext(), String.format(getString(R.string.filedetails_renamed_in_upload_msg), newName), Toast.LENGTH_LONG);\r
-                        msg.show();\r
-                    }\r
-                    getSherlockActivity().removeStickyBroadcast(intent);    // not the best place to do this; a small refactorization of BroadcastReceivers should be done\r
-                    updateFileDetails(false);    // it updates the buttons; must be called although !uploadWasFine; interrupted uploads still leave an incomplete file in the server\r
-                }\r
-            }\r
-        }\r
-    }\r
-    \r
-\r
-    // this is a temporary class for sharing purposes, it need to be replaced in transfer service\r
-    /*\r
-    @SuppressWarnings("unused")\r
-    private class ShareRunnable implements Runnable {\r
-        private String mPath;\r
-\r
-        public ShareRunnable(String path) {\r
-            mPath = path;\r
-        }\r
-        \r
-        public void run() {\r
-            AccountManager am = AccountManager.get(getActivity());\r
-            Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity());\r
-            OwnCloudVersion ocv = new OwnCloudVersion(am.getUserData(account, AccountAuthenticator.KEY_OC_VERSION));\r
-            String url = am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL) + AccountUtils.getWebdavPath(ocv);\r
-\r
-            Log.d("share", "sharing for version " + ocv.toString());\r
-\r
-            if (ocv.compareTo(new OwnCloudVersion(0x040000)) >= 0) {\r
-                String APPS_PATH = "/apps/files_sharing/";\r
-                String SHARE_PATH = "ajax/share.php";\r
-\r
-                String SHARED_PATH = "/apps/files_sharing/get.php?token=";\r
-                \r
-                final String WEBDAV_SCRIPT = "webdav.php";\r
-                final String WEBDAV_FILES_LOCATION = "/files/";\r
-                \r
-                WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(account, getActivity().getApplicationContext());\r
-                HttpConnectionManagerParams params = new HttpConnectionManagerParams();\r
-                params.setMaxConnectionsPerHost(wc.getHostConfiguration(), 5);\r
-\r
-                //wc.getParams().setParameter("http.protocol.single-cookie-header", true);\r
-                //wc.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);\r
-\r
-                PostMethod post = new PostMethod(am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL) + APPS_PATH + SHARE_PATH);\r
-\r
-                post.addRequestHeader("Content-type","application/x-www-form-urlencoded; charset=UTF-8" );\r
-                post.addRequestHeader("Referer", am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL));\r
-                List<NameValuePair> formparams = new ArrayList<NameValuePair>();\r
-                Log.d("share", mPath+"");\r
-                formparams.add(new BasicNameValuePair("sources",mPath));\r
-                formparams.add(new BasicNameValuePair("uid_shared_with", "public"));\r
-                formparams.add(new BasicNameValuePair("permissions", "0"));\r
-                post.setRequestEntity(new StringRequestEntity(URLEncodedUtils.format(formparams, HTTP.UTF_8)));\r
-\r
-                int status;\r
-                try {\r
-                    PropFindMethod find = new PropFindMethod(url+"/");\r
-                    find.addRequestHeader("Referer", am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL));\r
-                    Log.d("sharer", ""+ url+"/");\r
-                    \r
-                    for (org.apache.commons.httpclient.Header a : find.getRequestHeaders()) {\r
-                        Log.d("sharer-h", a.getName() + ":"+a.getValue());\r
-                    }\r
-                    \r
-                    int status2 = wc.executeMethod(find);\r
-\r
-                    Log.d("sharer", "propstatus "+status2);\r
-                    \r
-                    GetMethod get = new GetMethod(am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL) + "/");\r
-                    get.addRequestHeader("Referer", am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL));\r
-                    \r
-                    status2 = wc.executeMethod(get);\r
-\r
-                    Log.d("sharer", "getstatus "+status2);\r
-                    Log.d("sharer", "" + get.getResponseBodyAsString());\r
-                    \r
-                    for (org.apache.commons.httpclient.Header a : get.getResponseHeaders()) {\r
-                        Log.d("sharer", a.getName() + ":"+a.getValue());\r
-                    }\r
-\r
-                    status = wc.executeMethod(post);\r
-                    for (org.apache.commons.httpclient.Header a : post.getRequestHeaders()) {\r
-                        Log.d("sharer-h", a.getName() + ":"+a.getValue());\r
-                    }\r
-                    for (org.apache.commons.httpclient.Header a : post.getResponseHeaders()) {\r
-                        Log.d("sharer", a.getName() + ":"+a.getValue());\r
-                    }\r
-                    String resp = post.getResponseBodyAsString();\r
-                    Log.d("share", ""+post.getURI().toString());\r
-                    Log.d("share", "returned status " + status);\r
-                    Log.d("share", " " +resp);\r
-                    \r
-                    if(status != HttpStatus.SC_OK ||resp == null || resp.equals("") || resp.startsWith("false")) {\r
-                        return;\r
-                     }\r
-\r
-                    JSONObject jsonObject = new JSONObject (resp);\r
-                    String jsonStatus = jsonObject.getString("status");\r
-                    if(!jsonStatus.equals("success")) throw new Exception("Error while sharing file status != success");\r
-                    \r
-                    String token = jsonObject.getString("data");\r
-                    String uri = am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL) + SHARED_PATH + token; \r
-                    Log.d("Actions:shareFile ok", "url: " + uri);   \r
-                    \r
-                } catch (Exception e) {\r
-                    e.printStackTrace();\r
-                }\r
-                \r
-            } else if (ocv.compareTo(new OwnCloudVersion(0x030000)) >= 0) {\r
-                \r
-            }\r
-        }\r
-    }\r
-    */\r
-    \r
-    public void onDismiss(EditNameDialog dialog) {\r
-        if (dialog.getResult()) {\r
-            String newFilename = dialog.getNewFilename();\r
-            Log.d(TAG, "name edit dialog dismissed with new name " + newFilename);\r
-            mLastRemoteOperation = new RenameFileOperation( mFile, \r
-                                                            mAccount, \r
-                                                            newFilename, \r
-                                                            new FileDataStorageManager(mAccount, getActivity().getContentResolver()));\r
-            mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());\r
-            boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;\r
-            getActivity().showDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);\r
-        }\r
-    }\r
-    \r
-    \r
-    class BitmapLoader extends AsyncTask<String, Void, Bitmap> {\r
-        @SuppressLint({ "NewApi", "NewApi", "NewApi" }) // to avoid Lint errors since Android SDK r20\r
-               @Override\r
-        protected Bitmap doInBackground(String... params) {\r
-            Bitmap result = null;\r
-            if (params.length != 1) return result;\r
-            String storagePath = params[0];\r
-            try {\r
-\r
-                BitmapFactory.Options options = new Options();\r
-                options.inScaled = true;\r
-                options.inPurgeable = true;\r
-                options.inJustDecodeBounds = true;\r
-                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {\r
-                    options.inPreferQualityOverSpeed = false;\r
-                }\r
-                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {\r
-                    options.inMutable = false;\r
-                }\r
-\r
-                result = BitmapFactory.decodeFile(storagePath, options);\r
-                options.inJustDecodeBounds = false;\r
-\r
-                int width = options.outWidth;\r
-                int height = options.outHeight;\r
-                int scale = 1;\r
-                if (width >= 2048 || height >= 2048) {\r
-                    scale = (int) Math.ceil((Math.ceil(Math.max(height, width) / 2048.)));\r
-                    options.inSampleSize = scale;\r
-                }\r
-                Display display = getActivity().getWindowManager().getDefaultDisplay();\r
-                Point size = new Point();\r
-                int screenwidth;\r
-                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR2) {\r
-                    display.getSize(size);\r
-                    screenwidth = size.x;\r
-                } else {\r
-                    screenwidth = display.getWidth();\r
-                }\r
-\r
-                Log.e("ASD", "W " + width + " SW " + screenwidth);\r
-\r
-                if (width > screenwidth) {\r
-                    scale = (int) Math.ceil((float)width / screenwidth);\r
-                    options.inSampleSize = scale;\r
-                }\r
-\r
-                result = BitmapFactory.decodeFile(storagePath, options);\r
-\r
-                Log.e("ASD", "W " + options.outWidth + " SW " + options.outHeight);\r
-\r
-            } catch (OutOfMemoryError e) {\r
-                result = null;\r
-                Log.e(TAG, "Out of memory occured for file with size " + storagePath);\r
-                \r
-            } catch (NoSuchFieldError e) {\r
-                result = null;\r
-                Log.e(TAG, "Error from access to unexisting field despite protection " + storagePath);\r
-                \r
-            } catch (Throwable t) {\r
-                result = null;\r
-                Log.e(TAG, "Unexpected error while creating image preview " + storagePath, t);\r
-            }\r
-            return result;\r
-        }\r
-        @Override\r
-        protected void onPostExecute(Bitmap result) {\r
-            if (result != null && mPreview != null) {\r
-                mPreview.setImageBitmap(result);\r
-            }\r
-        }\r
-        \r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {\r
-        if (operation instanceof RemoveFileOperation) {\r
-            onRemoveFileOperationFinish((RemoveFileOperation)operation, result);\r
-                \r
-        } else if (operation instanceof RenameFileOperation) {\r
-            onRenameFileOperationFinish((RenameFileOperation)operation, result);\r
-                \r
-        } else if (operation instanceof SynchronizeFileOperation) {\r
-            onSynchronizeFileOperationFinish((SynchronizeFileOperation)operation, result);\r
-        }\r
-    }\r
-    \r
-    \r
-    private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) {\r
-        boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;\r
-        getActivity().dismissDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);\r
-        \r
-        if (result.isSuccess()) {\r
-            Toast msg = Toast.makeText(getActivity().getApplicationContext(), R.string.remove_success_msg, Toast.LENGTH_LONG);\r
-            msg.show();\r
-            if (inDisplayActivity) {\r
-                // double pane\r
-                FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction();\r
-                transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null)); // empty FileDetailFragment\r
-                transaction.commit();\r
-                mContainerActivity.onFileStateChanged();\r
-            } else {\r
-                getActivity().finish();\r
-            }\r
-                \r
-        } else {\r
-            Toast msg = Toast.makeText(getActivity(), R.string.remove_fail_msg, Toast.LENGTH_LONG); \r
-            msg.show();\r
-            if (result.isSslRecoverableException()) {\r
-                // TODO show the SSL warning dialog\r
-            }\r
-        }\r
-    }\r
-    \r
-    private void onRenameFileOperationFinish(RenameFileOperation operation, RemoteOperationResult result) {\r
-        boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;\r
-        getActivity().dismissDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);\r
-        \r
-        if (result.isSuccess()) {\r
-            updateFileDetails(((RenameFileOperation)operation).getFile(), mAccount);\r
-            mContainerActivity.onFileStateChanged();\r
-            \r
-        } else {\r
-            if (result.getCode().equals(ResultCode.INVALID_LOCAL_FILE_NAME)) {\r
-                Toast msg = Toast.makeText(getActivity(), R.string.rename_local_fail_msg, Toast.LENGTH_LONG); \r
-                msg.show();\r
-                // TODO throw again the new rename dialog\r
-            } else {\r
-                Toast msg = Toast.makeText(getActivity(), R.string.rename_server_fail_msg, Toast.LENGTH_LONG); \r
-                msg.show();\r
-                if (result.isSslRecoverableException()) {\r
-                    // TODO show the SSL warning dialog\r
-                }\r
-            }\r
-        }\r
-    }\r
-    \r
-    private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation, RemoteOperationResult result) {\r
-        boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;\r
-        getActivity().dismissDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);\r
-\r
-        if (!result.isSuccess()) {\r
-            if (result.getCode() == ResultCode.SYNC_CONFLICT) {\r
-                Intent i = new Intent(getActivity(), ConflictsResolveActivity.class);\r
-                i.putExtra(ConflictsResolveActivity.EXTRA_FILE, mFile);\r
-                i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, mAccount);\r
-                startActivity(i);\r
-                \r
-            } else {\r
-                Toast msg = Toast.makeText(getActivity(), R.string.sync_file_fail_msg, Toast.LENGTH_LONG); \r
-                msg.show();\r
-            }\r
-            \r
-            if (mFile.isDown()) {\r
-                setButtonsForDown();\r
-                \r
-            } else {\r
-                setButtonsForRemote();\r
-            }\r
-            \r
-        } else {\r
-            if (operation.transferWasRequested()) {\r
-                mContainerActivity.onFileStateChanged();    // this is not working; FileDownloader won't do NOTHING at all until this method finishes, so \r
-                                                            // checking the service to see if the file is downloading results in FALSE\r
-            } else {\r
-                Toast msg = Toast.makeText(getActivity(), R.string.sync_file_nothing_to_do_msg, Toast.LENGTH_LONG); \r
-                msg.show();\r
-                if (mFile.isDown()) {\r
-                    setButtonsForDown();\r
-                    \r
-                } else {\r
-                    setButtonsForRemote();\r
-                }\r
-            }\r
-        }\r
-    }\r
-\r
-}\r
+            case R.id.fdKeepInSync: {
+                CheckBox cb = (CheckBox) getView().findViewById(R.id.fdKeepInSync);
+                mFile.setKeepInSync(cb.isChecked());
+                mStorageManager.saveFile(mFile);
+                
+                /// register the OCFile instance in the observer service to monitor local updates;
+                /// if necessary, the file is download 
+                Intent intent = new Intent(getActivity().getApplicationContext(),
+                                           FileObserverService.class);
+                intent.putExtra(FileObserverService.KEY_FILE_CMD,
+                           (cb.isChecked()?
+                                   FileObserverService.CMD_ADD_OBSERVED_FILE:
+                                   FileObserverService.CMD_DEL_OBSERVED_FILE));
+                intent.putExtra(FileObserverService.KEY_CMD_ARG_FILE, mFile);
+                intent.putExtra(FileObserverService.KEY_CMD_ARG_ACCOUNT, mAccount);
+                getActivity().startService(intent);
+                
+                if (mFile.keepInSync()) {
+                    onClick(getView().findViewById(R.id.fdDownloadBtn));    // force an immediate synchronization
+                }
+                break;
+            }
+            case R.id.fdRenameBtn: {
+                String fileName = mFile.getFileName();
+                int extensionStart = mFile.isDirectory() ? -1 : fileName.lastIndexOf(".");
+                int selectionEnd = (extensionStart >= 0) ? extensionStart : fileName.length();
+                EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), fileName, 0, selectionEnd, this);
+                dialog.show(getFragmentManager(), "nameeditdialog");
+                break;
+            }   
+            case R.id.fdRemoveBtn: {
+                ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
+                        R.string.confirmation_remove_alert,
+                        new String[]{mFile.getFileName()},
+                        mFile.isDown() ? R.string.confirmation_remove_remote_and_local : R.string.confirmation_remove_remote,
+                        mFile.isDown() ? R.string.confirmation_remove_local : -1,
+                        R.string.common_cancel);
+                confDialog.setOnConfirmationListener(this);
+                confDialog.show(getFragmentManager(), FTAG_CONFIRMATION);
+                break;
+            }
+            case R.id.fdOpenBtn: {
+                openFile();
+                break;
+            }
+            default:
+                Log_OC.e(TAG, "Incorrect view clicked!");
+        }
+        
+    }\r
+    \r
+    /**
+     * Opens mFile.
+     */
+    private void openFile() {
+        
+        String storagePath = mFile.getStoragePath();
+        String encodedStoragePath = WebdavUtils.encodePath(storagePath);
+        try {
+            Intent i = new Intent(Intent.ACTION_VIEW);
+            i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mFile.getMimetype());
+            i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+            startActivity(i);
+            
+        } catch (Throwable t) {
+            Log_OC.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + mFile.getMimetype());
+            boolean toastIt = true; 
+            String mimeType = "";
+            try {
+                Intent i = new Intent(Intent.ACTION_VIEW);
+                mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1));
+                if (mimeType == null || !mimeType.equals(mFile.getMimetype())) {
+                    if (mimeType != null) {
+                        i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mimeType);
+                    } else {
+                        // desperate try
+                        i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), "*/*");
+                    }
+                    i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+                    startActivity(i);
+                    toastIt = false;
+                }
+                
+            } catch (IndexOutOfBoundsException e) {
+                Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + storagePath);
+                
+            } catch (ActivityNotFoundException e) {
+                Log_OC.e(TAG, "No activity found to handle: " + storagePath + " with MIME type " + mimeType + " obtained from extension");
+                
+            } catch (Throwable th) {
+                Log_OC.e(TAG, "Unexpected problem when opening: " + storagePath, th);
+                
+            } finally {
+                if (toastIt) {
+                    Toast.makeText(getActivity(), "There is no application to handle file " + mFile.getFileName(), Toast.LENGTH_SHORT).show();
+                }
+            }
+            
+        }
+    }
+
+    @Override
+    public void onConfirmation(String callerTag) {
+        if (callerTag.equals(FTAG_CONFIRMATION)) {
+            if (mStorageManager.getFileById(mFile.getFileId()) != null) {
+                mLastRemoteOperation = new RemoveFileOperation( mFile, 
+                                                                true, 
+                                                                mStorageManager);
+                mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
+                
+                boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;
+                getActivity().showDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);
+            }
+        }
+    }
+    
+    @Override
+    public void onNeutral(String callerTag) {
+        File f = null;
+        if (mFile.isDown() && (f = new File(mFile.getStoragePath())).exists()) {
+            f.delete();
+            mFile.setStoragePath(null);
+            mStorageManager.saveFile(mFile);
+            updateFileDetails(mFile, mAccount);
+        }
+    }
+    
+    @Override
+    public void onCancel(String callerTag) {
+        Log_OC.d(TAG, "REMOVAL CANCELED");
+    }
+    
+    
+    /**
+     * Check if the fragment was created with an empty layout. An empty fragment can't show file details, must be replaced.
+     * 
+     * @return  True when the fragment was created with the empty layout.
+     */
+    public boolean isEmpty() {
+        return (mLayout == R.layout.file_details_empty || mFile == null || mAccount == null);
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    public OCFile getFile(){
+        return mFile;
+    }
+    
+    /**
+     * Use this method to signal this Activity that it shall update its view.
+     * 
+     * @param file : An {@link OCFile}
+     */
+    public void updateFileDetails(OCFile file, Account ocAccount) {
+        mFile = file;
+        if (ocAccount != null && ( 
+                mStorageManager == null || 
+                (mAccount != null && !mAccount.equals(ocAccount))
+           )) {
+            mStorageManager = new FileDataStorageManager(ocAccount, getActivity().getApplicationContext().getContentResolver());
+        }
+        mAccount = ocAccount;
+        updateFileDetails(false, false);
+    }
+
+    /**
+     * Updates the view with all relevant details about that file.
+     *
+     * TODO Remove parameter when the transferring state of files is kept in database. 
+     * 
+     * TODO REFACTORING! this method called 5 times before every time the fragment is shown! 
+     * 
+     * @param transferring      Flag signaling if the file should be considered as downloading or uploading, 
+     *                          although {@link FileDownloaderBinder#isDownloading(Account, OCFile)}  and 
+     *                          {@link FileUploaderBinder#isUploading(Account, OCFile)} return false.
+     *                          
+     * @param refresh           If 'true', try to refresh the hold file from the database
+     */
+    public void updateFileDetails(boolean transferring, boolean refresh) {
+
+        if (readyToShow()) {
+            
+            if (refresh && mStorageManager != null) {
+                mFile = mStorageManager.getFileByPath(mFile.getRemotePath());
+            }
+            
+            // set file details
+            setFilename(mFile.getFileName());
+            setFiletype(mFile.getMimetype());
+            setFilesize(mFile.getFileLength());
+            if(ocVersionSupportsTimeCreated()){
+                setTimeCreated(mFile.getCreationTimestamp());
+            }
+           
+            setTimeModified(mFile.getModificationTimestamp());
+            
+            CheckBox cb = (CheckBox)getView().findViewById(R.id.fdKeepInSync);
+            cb.setChecked(mFile.keepInSync());
+
+            // configure UI for depending upon local state of the file
+            //if (FileDownloader.isDownloading(mAccount, mFile.getRemotePath()) || FileUploader.isUploading(mAccount, mFile.getRemotePath())) {
+            FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
+            FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
+            if (transferring || (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile)) || (uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile))) {
+                setButtonsForTransferring();
+                
+            } else if (mFile.isDown()) {
+                
+                setButtonsForDown();
+                
+            } else {
+                // TODO load default preview image; when the local file is removed, the preview remains there
+                setButtonsForRemote();
+            }
+        }
+        getView().invalidate();
+    }
+    
+    /**
+     * Checks if the fragment is ready to show details of a OCFile
+     *  
+     * @return  'True' when the fragment is ready to show details of a file
+     */
+    private boolean readyToShow() {
+        return (mFile != null && mAccount != null && mLayout == R.layout.file_details_fragment);        
+    }
+
+
+    /**
+     * Updates the filename in view
+     * @param filename to set
+     */
+    private void setFilename(String filename) {
+        TextView tv = (TextView) getView().findViewById(R.id.fdFilename);
+        if (tv != null)
+            tv.setText(filename);
+    }
+
+    /**
+     * Updates the MIME type in view
+     * @param mimetype to set
+     */
+    private void setFiletype(String mimetype) {
+        TextView tv = (TextView) getView().findViewById(R.id.fdType);
+        if (tv != null) {
+            String printableMimetype = DisplayUtils.convertMIMEtoPrettyPrint(mimetype);;        
+            tv.setText(printableMimetype);
+        }
+        ImageView iv = (ImageView) getView().findViewById(R.id.fdIcon);
+        if (iv != null) {
+            iv.setImageResource(DisplayUtils.getResourceId(mimetype));
+        }
+    }
+
+    /**
+     * Updates the file size in view
+     * @param filesize in bytes to set
+     */
+    private void setFilesize(long filesize) {
+        TextView tv = (TextView) getView().findViewById(R.id.fdSize);
+        if (tv != null)
+            tv.setText(DisplayUtils.bytesToHumanReadable(filesize));
+    }
+    
+    /**
+     * Updates the time that the file was created in view
+     * @param milliseconds Unix time to set
+     */
+    private void setTimeCreated(long milliseconds){
+        TextView tv = (TextView) getView().findViewById(R.id.fdCreated);
+        TextView tvLabel = (TextView) getView().findViewById(R.id.fdCreatedLabel);
+        if(tv != null){
+            tv.setText(DisplayUtils.unixTimeToHumanReadable(milliseconds));
+            tv.setVisibility(View.VISIBLE);
+            tvLabel.setVisibility(View.VISIBLE);
+        }
+    }
+    
+    /**
+     * Updates the time that the file was last modified
+     * @param milliseconds Unix time to set
+     */
+    private void setTimeModified(long milliseconds){
+        TextView tv = (TextView) getView().findViewById(R.id.fdModified);
+        if(tv != null){
+            tv.setText(DisplayUtils.unixTimeToHumanReadable(milliseconds));
+        }
+    }
+    
+    /**
+     * Enables or disables buttons for a file being downloaded
+     */
+    private void setButtonsForTransferring() {
+        if (!isEmpty()) {
+            Button downloadButton = (Button) getView().findViewById(R.id.fdDownloadBtn);
+            downloadButton.setText(R.string.common_cancel);
+            //downloadButton.setEnabled(false);
+        
+            // let's protect the user from himself ;)
+            ((Button) getView().findViewById(R.id.fdOpenBtn)).setEnabled(false);
+            ((Button) getView().findViewById(R.id.fdRenameBtn)).setEnabled(false);
+            ((Button) getView().findViewById(R.id.fdRemoveBtn)).setEnabled(false);
+            getView().findViewById(R.id.fdKeepInSync).setEnabled(false);
+            
+            // show the progress bar for the transfer
+            ProgressBar progressBar = (ProgressBar)getView().findViewById(R.id.fdProgressBar);
+            progressBar.setVisibility(View.VISIBLE);
+            TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText);
+            progressText.setVisibility(View.VISIBLE);
+            FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
+            FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
+            if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile)) {
+                progressText.setText(R.string.downloader_download_in_progress_ticker);
+            } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile)) {
+                progressText.setText(R.string.uploader_upload_in_progress_ticker);
+            }
+        }
+    }
+
+    /**
+     * Enables or disables buttons for a file locally available 
+     */
+    private void setButtonsForDown() {
+        if (!isEmpty()) {
+            Button downloadButton = (Button) getView().findViewById(R.id.fdDownloadBtn);
+            downloadButton.setText(R.string.filedetails_sync_file);
+        
+            ((Button) getView().findViewById(R.id.fdOpenBtn)).setEnabled(true);
+            ((Button) getView().findViewById(R.id.fdRenameBtn)).setEnabled(true);
+            ((Button) getView().findViewById(R.id.fdRemoveBtn)).setEnabled(true);
+            getView().findViewById(R.id.fdKeepInSync).setEnabled(true);
+            
+            // hides the progress bar
+            ProgressBar progressBar = (ProgressBar)getView().findViewById(R.id.fdProgressBar);
+            progressBar.setVisibility(View.GONE);
+            TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText);
+            progressText.setVisibility(View.GONE);
+        }
+    }
+
+    /**
+     * Enables or disables buttons for a file not locally available 
+     */
+    private void setButtonsForRemote() {
+        if (!isEmpty()) {
+            Button downloadButton = (Button) getView().findViewById(R.id.fdDownloadBtn);
+            downloadButton.setText(R.string.filedetails_download);
+            
+            ((Button) getView().findViewById(R.id.fdOpenBtn)).setEnabled(false);
+            ((Button) getView().findViewById(R.id.fdRenameBtn)).setEnabled(true);
+            ((Button) getView().findViewById(R.id.fdRemoveBtn)).setEnabled(true);
+            getView().findViewById(R.id.fdKeepInSync).setEnabled(true);
+            
+            // hides the progress bar
+            ProgressBar progressBar = (ProgressBar)getView().findViewById(R.id.fdProgressBar);
+            progressBar.setVisibility(View.GONE);
+            TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText);
+            progressText.setVisibility(View.GONE);
+        }
+    }
+    
+
+    /**
+     * In ownCloud 3.X.X and 4.X.X there is a bug that SabreDAV does not return
+     * the time that the file was created. There is a chance that this will
+     * be fixed in future versions. Use this method to check if this version of
+     * ownCloud has this fix.
+     * @return True, if ownCloud the ownCloud version is supporting creation time
+     */
+    private boolean ocVersionSupportsTimeCreated(){
+        /*if(mAccount != null){
+            AccountManager accManager = (AccountManager) getActivity().getSystemService(Context.ACCOUNT_SERVICE);
+            OwnCloudVersion ocVersion = new OwnCloudVersion(accManager
+                    .getUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION));
+            if(ocVersion.compareTo(new OwnCloudVersion(0x030000)) < 0) {
+                return true;
+            }
+        }*/
+        return false;
+    }
+    
+
+    /**
+     * Once the file upload has finished -> update view
+     * 
+     * Being notified about the finish of an upload is necessary for the next sequence:
+     *   1. Upload a big file.
+     *   2. Force a synchronization; if it finished before the upload, the file in transfer will be included in the local database and in the file list
+     *      of its containing folder; the the server includes it in the PROPFIND requests although it's not fully upload. 
+     *   3. Click the file in the list to see its details.
+     *   4. Wait for the upload finishes; at this moment, the details view must be refreshed to enable the action buttons.
+     */
+    private class UploadFinishReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String accountName = intent.getStringExtra(FileUploader.ACCOUNT_NAME);
+
+            if (!isEmpty() && accountName.equals(mAccount.name)) {
+                boolean uploadWasFine = intent.getBooleanExtra(FileUploader.EXTRA_UPLOAD_RESULT, false);
+                String uploadRemotePath = intent.getStringExtra(FileUploader.EXTRA_REMOTE_PATH);
+                boolean renamedInUpload = mFile.getRemotePath().equals(intent.getStringExtra(FileUploader.EXTRA_OLD_REMOTE_PATH));
+                if (mFile.getRemotePath().equals(uploadRemotePath) ||
+                    renamedInUpload) {
+                    if (uploadWasFine) {
+                        mFile = mStorageManager.getFileByPath(uploadRemotePath);
+                    }
+                    if (renamedInUpload) {
+                        String newName = (new File(uploadRemotePath)).getName();
+                        Toast msg = Toast.makeText(getActivity().getApplicationContext(), String.format(getString(R.string.filedetails_renamed_in_upload_msg), newName), Toast.LENGTH_LONG);
+                        msg.show();
+                    }
+                    getSherlockActivity().removeStickyBroadcast(intent);    // not the best place to do this; a small refactorization of BroadcastReceivers should be done
+                    updateFileDetails(false, false);    // it updates the buttons; must be called although !uploadWasFine; interrupted uploads still leave an incomplete file in the server
+                }
+            }
+        }
+    }
+    
+
+    public void onDismiss(EditNameDialog dialog) {
+        if (dialog.getResult()) {
+            String newFilename = dialog.getNewFilename();
+            Log_OC.d(TAG, "name edit dialog dismissed with new name " + newFilename);
+            mLastRemoteOperation = new RenameFileOperation( mFile, 
+                                                            mAccount, 
+                                                            newFilename, 
+                                                            new FileDataStorageManager(mAccount, getActivity().getContentResolver()));
+            mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
+            boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;
+            getActivity().showDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);
+        }
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
+        if (operation.equals(mLastRemoteOperation)) {
+            if (operation instanceof RemoveFileOperation) {
+                onRemoveFileOperationFinish((RemoveFileOperation)operation, result);
+                
+            } else if (operation instanceof RenameFileOperation) {
+                onRenameFileOperationFinish((RenameFileOperation)operation, result);
+                
+            } else if (operation instanceof SynchronizeFileOperation) {
+                onSynchronizeFileOperationFinish((SynchronizeFileOperation)operation, result);
+            }
+        }
+    }
+    
+    
+    private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) {
+        boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;
+        getActivity().dismissDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);
+        
+        if (result.isSuccess()) {
+            Toast msg = Toast.makeText(getActivity().getApplicationContext(), R.string.remove_success_msg, Toast.LENGTH_LONG);
+            msg.show();
+            if (inDisplayActivity) {
+                // double pane
+                FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction();
+                transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null)); // empty FileDetailFragment
+                transaction.commit();
+                mContainerActivity.onFileStateChanged();
+            } else {
+                getActivity().finish();
+            }
+                
+        } else {
+            Toast msg = Toast.makeText(getActivity(), R.string.remove_fail_msg, Toast.LENGTH_LONG); 
+            msg.show();
+            if (result.isSslRecoverableException()) {
+                // TODO show the SSL warning dialog
+            }
+        }
+    }
+    
+    private void onRenameFileOperationFinish(RenameFileOperation operation, RemoteOperationResult result) {
+        boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;
+        getActivity().dismissDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);
+        
+        if (result.isSuccess()) {
+            updateFileDetails(((RenameFileOperation)operation).getFile(), mAccount);
+            mContainerActivity.onFileStateChanged();
+            
+        } else {
+            if (result.getCode().equals(ResultCode.INVALID_LOCAL_FILE_NAME)) {
+                Toast msg = Toast.makeText(getActivity(), R.string.rename_local_fail_msg, Toast.LENGTH_LONG); 
+                msg.show();
+                // TODO throw again the new rename dialog
+            } else {
+                Toast msg = Toast.makeText(getActivity(), R.string.rename_server_fail_msg, Toast.LENGTH_LONG); 
+                msg.show();
+                if (result.isSslRecoverableException()) {
+                    // TODO show the SSL warning dialog
+                }
+            }
+        }
+    }
+    
+    private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation, RemoteOperationResult result) {
+        boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;
+        getActivity().dismissDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);
+
+        if (!result.isSuccess()) {
+            if (result.getCode() == ResultCode.SYNC_CONFLICT) {
+                Intent i = new Intent(getActivity(), ConflictsResolveActivity.class);
+                i.putExtra(ConflictsResolveActivity.EXTRA_FILE, mFile);
+                i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, mAccount);
+                startActivity(i);
+                
+            } else {
+                Toast msg = Toast.makeText(getActivity(), R.string.sync_file_fail_msg, Toast.LENGTH_LONG); 
+                msg.show();
+            }
+            
+            if (mFile.isDown()) {
+                setButtonsForDown();
+                
+            } else {
+                setButtonsForRemote();
+            }
+            
+        } else {
+            if (operation.transferWasRequested()) {
+                setButtonsForTransferring();
+                mContainerActivity.onFileStateChanged();    // this is not working; FileDownloader won't do NOTHING at all until this method finishes, so 
+                                                            // checking the service to see if the file is downloading results in FALSE
+            } else {
+                Toast msg = Toast.makeText(getActivity(), R.string.sync_file_nothing_to_do_msg, Toast.LENGTH_LONG); 
+                msg.show();
+                if (mFile.isDown()) {
+                    setButtonsForDown();
+                    
+                } else {
+                    setButtonsForRemote();
+                }
+            }
+        }
+    }
+    
+
+    public void listenForTransferProgress() {
+        if (mProgressListener != null) {
+            if (mContainerActivity.getFileDownloaderBinder() != null) {
+                mContainerActivity.getFileDownloaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, mFile);
+            }
+            if (mContainerActivity.getFileUploaderBinder() != null) {
+                mContainerActivity.getFileUploaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, mFile);
+            }
+        }
+    }
+    
+    
+    public void leaveTransferProgress() {
+        if (mProgressListener != null) {
+            if (mContainerActivity.getFileDownloaderBinder() != null) {
+                mContainerActivity.getFileDownloaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, mFile);
+            }
+            if (mContainerActivity.getFileUploaderBinder() != null) {
+                mContainerActivity.getFileUploaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, mFile);
+            }
+        }
+    }
+
+
+    
+    /**
+     * Helper class responsible for updating the progress bar shown for file uploading or downloading  
+     * 
+     * @author David A. Velasco
+     */
+    private class ProgressListener implements OnDatatransferProgressListener {
+        int mLastPercent = 0;
+        WeakReference<ProgressBar> mProgressBar = null;
+        
+        ProgressListener(ProgressBar progressBar) {
+            mProgressBar = new WeakReference<ProgressBar>(progressBar);
+        }
+        
+        @Override
+        public void onTransferProgress(long progressRate) {
+            // old method, nothing here
+        };
+
+        @Override
+        public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filename) {
+            int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer));
+            if (percent != mLastPercent) {
+                ProgressBar pb = mProgressBar.get();
+                if (pb != null) {
+                    pb.setProgress(percent);
+                    pb.postInvalidate();
+                }
+            }
+            mLastPercent = percent;
+        }
+
+    };
+
+    /*
+    // this is a temporary class for sharing purposes, it need to be replaced in transfer service
+    @SuppressWarnings("unused")
+    private class ShareRunnable implements Runnable {
+        private String mPath;
+
+        public ShareRunnable(String path) {
+            mPath = path;
+        }
+        
+        public void run() {
+            AccountManager am = AccountManager.get(getActivity());
+            Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity());
+            OwnCloudVersion ocv = new OwnCloudVersion(am.getUserData(account, AccountAuthenticator.KEY_OC_VERSION));
+            String url = am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL) + AccountUtils.getWebdavPath(ocv);
+
+            Log_OC.d("share", "sharing for version " + ocv.toString());
+
+            if (ocv.compareTo(new OwnCloudVersion(0x040000)) >= 0) {
+                String APPS_PATH = "/apps/files_sharing/";
+                String SHARE_PATH = "ajax/share.php";
+
+                String SHARED_PATH = "/apps/files_sharing/get.php?token=";
+                
+                final String WEBDAV_SCRIPT = "webdav.php";
+                final String WEBDAV_FILES_LOCATION = "/files/";
+                
+                WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(account, getActivity().getApplicationContext());
+                HttpConnectionManagerParams params = new HttpConnectionManagerParams();
+                params.setMaxConnectionsPerHost(wc.getHostConfiguration(), 5);
+
+                //wc.getParams().setParameter("http.protocol.single-cookie-header", true);
+                //wc.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
+
+                PostMethod post = new PostMethod(am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL) + APPS_PATH + SHARE_PATH);
+
+                post.addRequestHeader("Content-type","application/x-www-form-urlencoded; charset=UTF-8" );
+                post.addRequestHeader("Referer", am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL));
+                List<NameValuePair> formparams = new ArrayList<NameValuePair>();
+                Log_OC.d("share", mPath+"");
+                formparams.add(new BasicNameValuePair("sources",mPath));
+                formparams.add(new BasicNameValuePair("uid_shared_with", "public"));
+                formparams.add(new BasicNameValuePair("permissions", "0"));
+                post.setRequestEntity(new StringRequestEntity(URLEncodedUtils.format(formparams, HTTP.UTF_8)));
+
+                int status;
+                try {
+                    PropFindMethod find = new PropFindMethod(url+"/");
+                    find.addRequestHeader("Referer", am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL));
+                    Log_OC.d("sharer", ""+ url+"/");
+                    
+                    for (org.apache.commons.httpclient.Header a : find.getRequestHeaders()) {
+                        Log_OC.d("sharer-h", a.getName() + ":"+a.getValue());
+                    }
+                    
+                    int status2 = wc.executeMethod(find);
+
+                    Log_OC.d("sharer", "propstatus "+status2);
+                    
+                    GetMethod get = new GetMethod(am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL) + "/");
+                    get.addRequestHeader("Referer", am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL));
+                    
+                    status2 = wc.executeMethod(get);
+
+                    Log_OC.d("sharer", "getstatus "+status2);
+                    Log_OC.d("sharer", "" + get.getResponseBodyAsString());
+                    
+                    for (org.apache.commons.httpclient.Header a : get.getResponseHeaders()) {
+                        Log_OC.d("sharer", a.getName() + ":"+a.getValue());
+                    }
+
+                    status = wc.executeMethod(post);
+                    for (org.apache.commons.httpclient.Header a : post.getRequestHeaders()) {
+                        Log_OC.d("sharer-h", a.getName() + ":"+a.getValue());
+                    }
+                    for (org.apache.commons.httpclient.Header a : post.getResponseHeaders()) {
+                        Log_OC.d("sharer", a.getName() + ":"+a.getValue());
+                    }
+                    String resp = post.getResponseBodyAsString();
+                    Log_OC.d("share", ""+post.getURI().toString());
+                    Log_OC.d("share", "returned status " + status);
+                    Log_OC.d("share", " " +resp);
+                    
+                    if(status != HttpStatus.SC_OK ||resp == null || resp.equals("") || resp.startsWith("false")) {
+                        return;
+                     }
+
+                    JSONObject jsonObject = new JSONObject (resp);
+                    String jsonStatus = jsonObject.getString("status");
+                    if(!jsonStatus.equals("success")) throw new Exception("Error while sharing file status != success");
+                    
+                    String token = jsonObject.getString("data");
+                    String uri = am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL) + SHARED_PATH + token; 
+                    Log_OC.d("Actions:shareFile ok", "url: " + uri);   
+                    
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+                
+            } else if (ocv.compareTo(new OwnCloudVersion(0x030000)) >= 0) {
+                
+            }
+        }
+    }
+    */
+    
+}
diff --git a/src/com/owncloud/android/ui/fragment/FileFragment.java b/src/com/owncloud/android/ui/fragment/FileFragment.java
new file mode 100644 (file)
index 0000000..3054951
--- /dev/null
@@ -0,0 +1,73 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2012-2013  ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License 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.ui.fragment;
+
+import android.content.Intent;
+import android.support.v4.app.Fragment;
+
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.ui.activity.TransferServiceGetter;
+
+/**
+ * Common methods for {@link Fragment}s containing {@link OCFile}s
+ * 
+ * @author David A. Velasco
+ *
+ */
+public interface FileFragment {
+    
+    /**
+     * Getter for the hold {@link OCFile}
+     * 
+     * @return The {@link OCFile} hold
+     */
+    public OCFile getFile();
+    
+    
+    /**
+     * Interface to implement by any Activity that includes some instance of FileFragment
+     * 
+     * @author David A. Velasco
+     */
+    public interface ContainerActivity extends TransferServiceGetter {
+
+        /**
+         * Callback method invoked when the detail fragment wants to notice its container 
+         * activity about a relevant state the file shown by the fragment.
+         * 
+         * Added to notify to FileDisplayActivity about the need of refresh the files list. 
+         * 
+         * Currently called when:
+         *  - a download is started;
+         *  - a rename is completed;
+         *  - a deletion is completed;
+         *  - the 'inSync' flag is changed;
+         */
+        public void onFileStateChanged();
+
+        /**
+         * Request the parent activity to show the details of an {@link OCFile}.
+         * 
+         * @param file      File to show details
+         */
+        public void showFragmentWithDetails(OCFile file);
+        
+        
+    }
+    
+}
index ab58463..9d87a45 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.\r
  *\r
  *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
+ *   it under the terms of the GNU General Public License version 2,\r
+ *   as published by the Free Software Foundation.\r
  *\r
  *   This program is distributed in the hope that it will be useful,\r
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
index 51f78b7..4e6affa 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -26,7 +25,6 @@ import com.owncloud.android.ui.adapter.LocalFileListAdapter;
 import android.app.Activity;
 import android.os.Bundle;
 import android.os.Environment;
-import android.util.Log;
 import android.util.SparseBooleanArray;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -35,6 +33,7 @@ import android.widget.AdapterView;
 import android.widget.ImageView;
 import android.widget.ListView;
 
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.R;
 
 /**
@@ -76,10 +75,10 @@ public class LocalFileListFragment extends FragmentListView {
      */
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        Log.i(TAG, "onCreateView() start");
+        Log_OC.i(TAG, "onCreateView() start");
         View v = super.onCreateView(inflater, container, savedInstanceState);
         getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
-        Log.i(TAG, "onCreateView() end");
+        Log_OC.i(TAG, "onCreateView() end");
         return v;
     }    
 
@@ -89,30 +88,30 @@ public class LocalFileListFragment extends FragmentListView {
      */
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
-        Log.i(TAG, "onActivityCreated() start");
+        Log_OC.i(TAG, "onActivityCreated() start");
         
         super.onCreate(savedInstanceState);
         mAdapter = new LocalFileListAdapter(mContainerActivity.getInitialDirectory(), getActivity());
         setListAdapter(mAdapter);
         
         if (savedInstanceState != null) {
-            Log.i(TAG, "savedInstanceState is not null");
+            Log_OC.i(TAG, "savedInstanceState is not null");
             int position = savedInstanceState.getInt(SAVED_LIST_POSITION);
             setReferencePosition(position);
         }
         
-        Log.i(TAG, "onActivityCreated() stop");
+        Log_OC.i(TAG, "onActivityCreated() stop");
     }
     
     
     @Override
     public void onSaveInstanceState(Bundle savedInstanceState) {
-        Log.i(TAG, "onSaveInstanceState() start");
+        Log_OC.i(TAG, "onSaveInstanceState() start");
         
         savedInstanceState.putInt(SAVED_LIST_POSITION, getReferencePosition());
         
         
-        Log.i(TAG, "onSaveInstanceState() stop");
+        Log_OC.i(TAG, "onSaveInstanceState() stop");
     }
     
     
@@ -144,7 +143,7 @@ public class LocalFileListFragment extends FragmentListView {
             }
             
         } else {
-            Log.w(TAG, "Null object in ListAdapter!!");
+            Log_OC.w(TAG, "Null object in ListAdapter!!");
         }
     }
 
@@ -183,7 +182,7 @@ public class LocalFileListFragment extends FragmentListView {
     
     /**
      * Lists the given directory on the view. When the input parameter is null,
-     * it will either refresh the last known directory, or list the root
+     * it will either refresh the last known directory. list the root
      * if there never was a directory.
      * 
      * @param directory     Directory to be listed
@@ -203,7 +202,7 @@ public class LocalFileListFragment extends FragmentListView {
         
         // if that's not a directory -> List its parent
         if(!directory.isDirectory()){
-            Log.w(TAG, "You see, that is not a directory -> " + directory.toString());
+            Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
             directory = directory.getParentFile();
         }
 
@@ -225,7 +224,7 @@ public class LocalFileListFragment extends FragmentListView {
         String [] result = null;
         SparseBooleanArray positions = mList.getCheckedItemPositions();
         if (positions.size() > 0) {
-            Log.d(TAG, "Returning " + positions.size() + " selected files");
+            Log_OC.d(TAG, "Returning " + positions.size() + " selected files");
             result = new String[positions.size()];
             for (int i=0; i<positions.size(); i++) {
                 result[i] = ((File) mList.getItemAtPosition(positions.keyAt(i))).getAbsolutePath();
index 0e5e879..f872803 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -23,6 +22,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 import com.owncloud.android.AccountUtils;
+import com.owncloud.android.Log_OC;
 import com.owncloud.android.R;
 import com.owncloud.android.datamodel.DataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
@@ -50,8 +50,6 @@ import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
-import android.support.v4.app.DialogFragment;
-import android.util.Log;
 import android.view.ContextMenu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
@@ -79,8 +77,6 @@ public class OCFileListFragment extends FragmentListView implements EditNameDial
     private Handler mHandler;
     private OCFile mTargetFile;
     
-    private DialogFragment mCurrentDialog;
-    
     /**
      * {@inheritDoc}
      */
@@ -100,14 +96,14 @@ public class OCFileListFragment extends FragmentListView implements EditNameDial
      */
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
-        Log.i(TAG, "onActivityCreated() start");
+        Log_OC.i(TAG, "onActivityCreated() start");
         
         super.onActivityCreated(savedInstanceState);
         mAdapter = new FileListListAdapter(mContainerActivity.getInitialDirectory(), mContainerActivity.getStorageManager(), getActivity(), mContainerActivity);
         setListAdapter(mAdapter);
         
         if (savedInstanceState != null) {
-            Log.i(TAG, "savedInstanceState is not null");
+            Log_OC.i(TAG, "savedInstanceState is not null");
             int position = savedInstanceState.getInt(SAVED_LIST_POSITION);
             setReferencePosition(position);
         }
@@ -117,18 +113,18 @@ public class OCFileListFragment extends FragmentListView implements EditNameDial
         
         mHandler = new Handler();
         
-        Log.i(TAG, "onActivityCreated() stop");
+        Log_OC.i(TAG, "onActivityCreated() stop");
     }
     
     
 
     @Override
     public void onSaveInstanceState(Bundle savedInstanceState) {
-        Log.i(TAG, "onSaveInstanceState() start");
+        Log_OC.i(TAG, "onSaveInstanceState() start");
         
         savedInstanceState.putInt(SAVED_LIST_POSITION, getReferencePosition());
         
-        Log.i(TAG, "onSaveInstanceState() stop");
+        Log_OC.i(TAG, "onSaveInstanceState() stop");
     }
     
     
@@ -149,7 +145,7 @@ public class OCFileListFragment extends FragmentListView implements EditNameDial
             }
             
         } else {
-            Log.d(TAG, "Null object in ListAdapter!!");
+            Log_OC.d(TAG, "Null object in ListAdapter!!");
         }
         
     }
@@ -161,7 +157,7 @@ public class OCFileListFragment extends FragmentListView implements EditNameDial
     public void onCreateContextMenu (ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
         super.onCreateContextMenu(menu, v, menuInfo);
         MenuInflater inflater = getActivity().getMenuInflater();
-        inflater.inflate(R.menu.file_context_menu, menu);
+        inflater.inflate(R.menu.file_actions_menu, menu);
         AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
         OCFile targetFile = (OCFile) mAdapter.getItem(info.position);
         List<Integer> toHide = new ArrayList<Integer>();    
@@ -170,46 +166,47 @@ public class OCFileListFragment extends FragmentListView implements EditNameDial
         MenuItem item = null;
         if (targetFile.isDirectory()) {
             // contextual menu for folders
-            toHide.add(R.id.open_file_item);
-            toHide.add(R.id.download_file_item);
-            toHide.add(R.id.cancel_download_item);
-            toHide.add(R.id.cancel_upload_item);
+            toHide.add(R.id.action_open_file_with);
+            toHide.add(R.id.action_download_file);
+            toHide.add(R.id.action_cancel_download);
+            toHide.add(R.id.action_cancel_upload);
+            toHide.add(R.id.action_see_details);
             if (    mContainerActivity.getFileDownloaderBinder().isDownloading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile) ||
                     mContainerActivity.getFileUploaderBinder().isUploading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile)           ) {
-                toDisable.add(R.id.rename_file_item);
-                toDisable.add(R.id.remove_file_item);
+                toDisable.add(R.id.action_rename_file);
+                toDisable.add(R.id.action_remove_file);
                 
             }
             
         } else {
             // contextual menu for regular files
             if (targetFile.isDown()) {
-                toHide.add(R.id.cancel_download_item);
-                toHide.add(R.id.cancel_upload_item);
-                item = menu.findItem(R.id.download_file_item);
+                toHide.add(R.id.action_cancel_download);
+                toHide.add(R.id.action_cancel_upload);
+                item = menu.findItem(R.id.action_download_file);
                 if (item != null) {
                     item.setTitle(R.string.filedetails_sync_file);
                 }
             } else {
-                toHide.add(R.id.open_file_item);
+                toHide.add(R.id.action_open_file_with);
             }
             if ( mContainerActivity.getFileDownloaderBinder().isDownloading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile)) {
-                toHide.add(R.id.download_file_item);
-                toHide.add(R.id.cancel_upload_item);
-                toDisable.add(R.id.open_file_item);
-                toDisable.add(R.id.rename_file_item);
-                toDisable.add(R.id.remove_file_item);
+                toHide.add(R.id.action_download_file);
+                toHide.add(R.id.action_cancel_upload);
+                toDisable.add(R.id.action_open_file_with);
+                toDisable.add(R.id.action_rename_file);
+                toDisable.add(R.id.action_remove_file);
                     
             } else if ( mContainerActivity.getFileUploaderBinder().isUploading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile)) {
-                toHide.add(R.id.download_file_item);
-                toHide.add(R.id.cancel_download_item);
-                toDisable.add(R.id.open_file_item);
-                toDisable.add(R.id.rename_file_item);
-                toDisable.add(R.id.remove_file_item);
+                toHide.add(R.id.action_download_file);
+                toHide.add(R.id.action_cancel_download);
+                toDisable.add(R.id.action_open_file_with);
+                toDisable.add(R.id.action_rename_file);
+                toDisable.add(R.id.action_remove_file);
                     
             } else {
-                toHide.add(R.id.cancel_download_item);
-                toHide.add(R.id.cancel_upload_item);
+                toHide.add(R.id.action_cancel_download);
+                toHide.add(R.id.action_cancel_upload);
             }
         }
 
@@ -238,12 +235,15 @@ public class OCFileListFragment extends FragmentListView implements EditNameDial
         AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();        
         mTargetFile = (OCFile) mAdapter.getItem(info.position);
         switch (item.getItemId()) {
-            case R.id.rename_file_item: {
-                EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), mTargetFile.getFileName(), this);
+            case R.id.action_rename_file: {
+                String fileName = mTargetFile.getFileName();
+                int extensionStart = mTargetFile.isDirectory() ? -1 : fileName.lastIndexOf(".");
+                int selectionEnd = (extensionStart >= 0) ? extensionStart : fileName.length();
+                EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), fileName, 0, selectionEnd, this);
                 dialog.show(getFragmentManager(), EditNameDialog.TAG);
                 return true;
             }
-            case R.id.remove_file_item: {
+            case R.id.action_remove_file: {
                 int messageStringId = R.string.confirmation_remove_alert;
                 int posBtnStringId = R.string.confirmation_remove_remote;
                 int neuBtnStringId = -1;
@@ -262,11 +262,10 @@ public class OCFileListFragment extends FragmentListView implements EditNameDial
                         neuBtnStringId,
                         R.string.common_cancel);
                 confDialog.setOnConfirmationListener(this);
-                mCurrentDialog = confDialog;
-                mCurrentDialog.show(getFragmentManager(), FileDetailFragment.FTAG_CONFIRMATION);
+                confDialog.show(getFragmentManager(), FileDetailFragment.FTAG_CONFIRMATION);
                 return true;
             }
-            case R.id.open_file_item: {
+            case R.id.action_open_file_with: {
                 String storagePath = mTargetFile.getStoragePath();
                 String encodedStoragePath = WebdavUtils.encodePath(storagePath);
                 try {
@@ -276,7 +275,7 @@ public class OCFileListFragment extends FragmentListView implements EditNameDial
                     startActivity(i);
                     
                 } catch (Throwable t) {
-                    Log.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + mTargetFile.getMimetype());
+                    Log_OC.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + mTargetFile.getMimetype());
                     boolean toastIt = true; 
                     String mimeType = "";
                     try {
@@ -295,13 +294,13 @@ public class OCFileListFragment extends FragmentListView implements EditNameDial
                         }
                         
                     } catch (IndexOutOfBoundsException e) {
-                        Log.e(TAG, "Trying to find out MIME type of a file without extension: " + storagePath);
+                        Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + storagePath);
                         
                     } catch (ActivityNotFoundException e) {
-                        Log.e(TAG, "No activity found to handle: " + storagePath + " with MIME type " + mimeType + " obtained from extension");
+                        Log_OC.e(TAG, "No activity found to handle: " + storagePath + " with MIME type " + mimeType + " obtained from extension");
                         
                     } catch (Throwable th) {
-                        Log.e(TAG, "Unexpected problem when opening: " + storagePath, th);
+                        Log_OC.e(TAG, "Unexpected problem when opening: " + storagePath, th);
                         
                     } finally {
                         if (toastIt) {
@@ -312,14 +311,14 @@ public class OCFileListFragment extends FragmentListView implements EditNameDial
                 }
                 return true;
             }
-            case R.id.download_file_item: {
+            case R.id.action_download_file: {
                 Account account = AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity());
                 RemoteOperation operation = new SynchronizeFileOperation(mTargetFile, null, mContainerActivity.getStorageManager(), account, true, false, getSherlockActivity());
                 operation.execute(account, getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
                 getSherlockActivity().showDialog(FileDisplayActivity.DIALOG_SHORT_WAIT);
                 return true;
             }
-            case R.id.cancel_download_item: {
+            case R.id.action_cancel_download: {
                 FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
                 Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity());
                 if (downloaderBinder != null && downloaderBinder.isDownloading(account, mTargetFile)) {
@@ -329,7 +328,7 @@ public class OCFileListFragment extends FragmentListView implements EditNameDial
                 }
                 return true;
             }
-            case R.id.cancel_upload_item: {
+            case R.id.action_cancel_upload: {
                 FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
                 Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity());
                 if (uploaderBinder != null && uploaderBinder.isUploading(account, mTargetFile)) {
@@ -339,6 +338,10 @@ public class OCFileListFragment extends FragmentListView implements EditNameDial
                 }
                 return true;
             }
+            case R.id.action_see_details: {
+                ((FileFragment.ContainerActivity)getActivity()).showFragmentWithDetails(mTargetFile);
+                return true;
+            }
             default:
                 return super.onContextItemSelected(item); 
         }
@@ -377,7 +380,7 @@ public class OCFileListFragment extends FragmentListView implements EditNameDial
     
     /**
      * Lists the given directory on the view. When the input parameter is null,
-     * it will either refresh the last known directory, or list the root
+     * it will either refresh the last known directory. list the root
      * if there never was a directory.
      * 
      * @param directory File to be listed
@@ -399,7 +402,7 @@ public class OCFileListFragment extends FragmentListView implements EditNameDial
         
             // If that's not a directory -> List its parent
             if(!directory.isDirectory()){
-                Log.w(TAG, "You see, that is not a directory -> " + directory.toString());
+                Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
                 directory = storageManager.getFileById(directory.getParentId());
             }
 
@@ -472,7 +475,7 @@ public class OCFileListFragment extends FragmentListView implements EditNameDial
     public void onDismiss(EditNameDialog dialog) {
         if (dialog.getResult()) {
             String newFilename = dialog.getNewFilename();
-            Log.d(TAG, "name edit dialog dismissed with new name " + newFilename);
+            Log_OC.d(TAG, "name edit dialog dismissed with new name " + newFilename);
             RemoteOperation operation = new RenameFileOperation(mTargetFile, 
                                                                 AccountUtils.getCurrentOwnCloudAccount(getActivity()), 
                                                                 newFilename, 
@@ -494,10 +497,6 @@ public class OCFileListFragment extends FragmentListView implements EditNameDial
                 
                 getActivity().showDialog(FileDisplayActivity.DIALOG_SHORT_WAIT);
             }
-            if (mCurrentDialog != null) {
-                mCurrentDialog.dismiss();
-                mCurrentDialog = null;
-            }
         }
     }
     
@@ -513,21 +512,13 @@ public class OCFileListFragment extends FragmentListView implements EditNameDial
             mTargetFile.setStoragePath(null);
             mContainerActivity.getStorageManager().saveFile(mTargetFile);
         }
-        if (mCurrentDialog != null) {
-            mCurrentDialog.dismiss();
-            mCurrentDialog = null;
-        }
         listDirectory();
         mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
     }
     
     @Override
     public void onCancel(String callerTag) {
-        Log.d(TAG, "REMOVAL CANCELED");
-        if (mCurrentDialog != null) {
-            mCurrentDialog.dismiss();
-            mCurrentDialog = null;
-        }
+        Log_OC.d(TAG, "REMOVAL CANCELED");
     }
 
 
diff --git a/src/com/owncloud/android/ui/preview/FileDownloadFragment.java b/src/com/owncloud/android/ui/preview/FileDownloadFragment.java
new file mode 100644 (file)
index 0000000..773350a
--- /dev/null
@@ -0,0 +1,396 @@
+/* ownCloud Android client application
+ * 
+ *   Copyright (C) 2012-2013  ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License 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.ui.preview;
+
+import java.lang.ref.WeakReference;
+
+import android.accounts.Account;
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v4.app.FragmentStatePagerAdapter;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.actionbarsherlock.app.SherlockFragment;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
+import com.owncloud.android.ui.fragment.FileFragment;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+
+import eu.alefzero.webdav.OnDatatransferProgressListener;
+
+/**
+ * This Fragment is used to monitor the progress of a file downloading.
+ * 
+ * @author David A. Velasco
+ */
+public class FileDownloadFragment extends SherlockFragment implements OnClickListener, FileFragment {
+
+    public static final String EXTRA_FILE = "FILE";
+    public static final String EXTRA_ACCOUNT = "ACCOUNT";
+    private static final String EXTRA_ERROR = "ERROR";
+
+    private FileFragment.ContainerActivity mContainerActivity;
+    
+    private View mView;
+    private OCFile mFile;
+    private Account mAccount;
+    private FileDataStorageManager mStorageManager;
+    
+    public ProgressListener mProgressListener;
+    private boolean mListening;
+    
+    private static final String TAG = FileDownloadFragment.class.getSimpleName();
+    
+    private boolean mIgnoreFirstSavedState;
+    private boolean mError;
+    
+
+    /**
+     * Creates an empty details fragment.
+     * 
+     * It's necessary to keep a public constructor without parameters; the system uses it when tries to reinstantiate a fragment automatically. 
+     */
+    public FileDownloadFragment() {
+        mFile = null;
+        mAccount = null;
+        mStorageManager = null;
+        mProgressListener = null;
+        mListening = false;
+        mIgnoreFirstSavedState = false;
+        mError = false;
+    }
+    
+    
+    /**
+     * Creates a details fragment.
+     * 
+     * When 'fileToDetail' or 'ocAccount' are null, creates a dummy layout (to use when a file wasn't tapped before).
+     * 
+     * @param fileToDetail      An {@link OCFile} to show in the fragment
+     * @param ocAccount         An ownCloud account; needed to start downloads
+     * @param ignoreFirstSavedState     Flag to work around an unexpected behaviour of {@link FragmentStatePagerAdapter}; TODO better solution 
+     */
+    public FileDownloadFragment(OCFile fileToDetail, Account ocAccount, boolean ignoreFirstSavedState) {
+        mFile = fileToDetail;
+        mAccount = ocAccount;
+        mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment 
+        mProgressListener = null;
+        mListening = false;
+        mIgnoreFirstSavedState = ignoreFirstSavedState;
+        mError = false;
+    }
+    
+    
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+    
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        super.onCreateView(inflater, container, savedInstanceState);
+        
+        if (savedInstanceState != null) {
+            if (!mIgnoreFirstSavedState) {
+                mFile = savedInstanceState.getParcelable(FileDownloadFragment.EXTRA_FILE);
+                mAccount = savedInstanceState.getParcelable(FileDownloadFragment.EXTRA_ACCOUNT);
+                mError = savedInstanceState.getBoolean(FileDownloadFragment.EXTRA_ERROR);
+            } else {
+                mIgnoreFirstSavedState = false;
+            }
+        }
+        
+        View view = null;
+        view = inflater.inflate(R.layout.file_download_fragment, container, false);
+        mView = view;
+        
+        ProgressBar progressBar = (ProgressBar)mView.findViewById(R.id.progressBar);
+        mProgressListener = new ProgressListener(progressBar);
+        
+        ((Button)mView.findViewById(R.id.cancelBtn)).setOnClickListener(this);
+        
+        if (mError) {
+            setButtonsForRemote();
+        } else {
+            setButtonsForTransferring();
+        }
+        
+        return view;
+    }
+    
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        try {
+            mContainerActivity = (ContainerActivity) activity;
+            
+        } catch (ClassCastException e) {
+            throw new ClassCastException(activity.toString() + " must implement " + FileFragment.ContainerActivity.class.getSimpleName());
+        }
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        if (mAccount != null) {
+            mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());;
+        }
+    }
+        
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putParcelable(FileDownloadFragment.EXTRA_FILE, mFile);
+        outState.putParcelable(FileDownloadFragment.EXTRA_ACCOUNT, mAccount);
+        outState.putBoolean(FileDownloadFragment.EXTRA_ERROR, mError);
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        listenForTransferProgress();
+    }
+    
+    @Override
+    public void onResume() {
+        super.onResume();
+    }
+
+
+    @Override
+    public void onPause() {
+        super.onPause();
+    }
+
+    
+    @Override
+    public void onStop() {
+        super.onStop();
+        leaveTransferProgress();
+    }
+    
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+    }
+    
+    
+    @Override
+    public View getView() {
+        if (!mListening) {
+            listenForTransferProgress();
+        }
+        return super.getView() == null ? mView : super.getView();
+    }
+
+    
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.cancelBtn: {
+                FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
+                if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile)) {
+                    downloaderBinder.cancel(mAccount, mFile);
+                    getActivity().finish(); // :)
+                    /*
+                    leaveTransferProgress();
+                    if (mFile.isDown()) {
+                        setButtonsForDown();
+                    } else {
+                        setButtonsForRemote();
+                    }
+                    */
+                }
+                break;
+            }
+            default:
+                Log_OC.e(TAG, "Incorrect view clicked!");
+        }
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    public OCFile getFile(){
+        return mFile;
+    }
+    
+    
+    /**
+     * Updates the view depending upon the state of the downloading file.
+     * 
+     * @param   transferring    When true, the view must be updated assuming that the holded file is 
+     *                          downloading, no matter what the downloaderBinder says.
+     */
+    public void updateView(boolean transferring) {
+        // configure UI for depending upon local state of the file
+        FileDownloaderBinder downloaderBinder = (mContainerActivity == null) ? null : mContainerActivity.getFileDownloaderBinder();
+        if (transferring || (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile))) {
+            setButtonsForTransferring();
+            
+        } else if (mFile.isDown()) {
+            
+            setButtonsForDown();
+            
+        } else {
+            setButtonsForRemote();
+        }
+        getView().invalidate();
+        
+    }
+
+
+    /**
+     * 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);
+        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 
+     */
+    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);
+        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 
+     * 
+     * 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);
+
+        // shows the error icon and message
+        getView().findViewById(R.id.errorText).setVisibility(View.VISIBLE);
+        getView().findViewById(R.id.error_image).setVisibility(View.VISIBLE);
+    }
+    
+
+    public void listenForTransferProgress() {
+        if (mProgressListener != null && !mListening) {
+            if (mContainerActivity.getFileDownloaderBinder() != null) {
+                mContainerActivity.getFileDownloaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, mFile);
+                mListening = true;
+                setButtonsForTransferring();
+            }
+        }
+    }
+    
+    
+    public void leaveTransferProgress() {
+        if (mProgressListener != null) {
+            if (mContainerActivity.getFileDownloaderBinder() != null) {
+                mContainerActivity.getFileDownloaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, mFile);
+                mListening = false;
+            }
+        }
+    }
+
+    
+    /**
+     * Helper class responsible for updating the progress bar shown for file uploading or downloading  
+     * 
+     * @author David A. Velasco
+     */
+    private class ProgressListener implements OnDatatransferProgressListener {
+        int mLastPercent = 0;
+        WeakReference<ProgressBar> mProgressBar = null;
+        
+        ProgressListener(ProgressBar progressBar) {
+            mProgressBar = new WeakReference<ProgressBar>(progressBar);
+        }
+        
+        @Override
+        public void onTransferProgress(long progressRate) {
+            // old method, nothing here
+        };
+
+        @Override
+        public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filename) {
+            int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer));
+            if (percent != mLastPercent) {
+                ProgressBar pb = mProgressBar.get();
+                if (pb != null) {
+                    pb.setProgress(percent);
+                    pb.postInvalidate();
+                }
+            }
+            mLastPercent = percent;
+        }
+
+    }
+
+
+    public void setError(boolean error) {
+        mError = error;
+    };
+    
+
+
+}
diff --git a/src/com/owncloud/android/ui/preview/PreviewImageActivity.java b/src/com/owncloud/android/ui/preview/PreviewImageActivity.java
new file mode 100644 (file)
index 0000000..9b3e7d8
--- /dev/null
@@ -0,0 +1,445 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2012-2013  ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License 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.ui.preview;
+
+import android.accounts.Account;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.support.v4.view.ViewPager;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.Window;
+import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.services.FileDownloader;
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
+import com.owncloud.android.ui.activity.FileDetailActivity;
+import com.owncloud.android.ui.fragment.FileDetailFragment;
+import com.owncloud.android.ui.fragment.FileFragment;
+
+import com.owncloud.android.AccountUtils;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+
+/**
+ *  Used as an utility to preview image files contained in an ownCloud account.
+ *  
+ *  @author David A. Velasco
+ */
+public class PreviewImageActivity extends SherlockFragmentActivity implements FileFragment.ContainerActivity, ViewPager.OnPageChangeListener, OnTouchListener {
+    
+    public static final int DIALOG_SHORT_WAIT = 0;
+
+    public static final String TAG = PreviewImageActivity.class.getSimpleName();
+    
+    public static final String KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW";
+    private static final String KEY_WAITING_FOR_BINDER = "WAITING_FOR_BINDER";
+    
+    private OCFile mFile;
+    private OCFile mParentFolder;  
+    private Account mAccount;
+    private DataStorageManager mStorageManager;
+    
+    private ViewPager mViewPager; 
+    private PreviewImagePagerAdapter mPreviewImagePagerAdapter;    
+    
+    private FileDownloaderBinder mDownloaderBinder = null;
+    private ServiceConnection mDownloadConnection, mUploadConnection = null;
+    private FileUploaderBinder mUploaderBinder = null;
+
+    private boolean mRequestWaitingForBinder;
+    
+    private DownloadFinishReceiver mDownloadFinishReceiver;
+
+    private boolean mFullScreen;
+    
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mFile = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE);
+        mAccount = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_ACCOUNT);
+        if (mFile == null) {
+            throw new IllegalStateException("Instanced with a NULL OCFile");
+        }
+        if (mAccount == null) {
+            throw new IllegalStateException("Instanced with a NULL ownCloud Account");
+        }
+        if (!mFile.isImage()) {
+            throw new IllegalArgumentException("Non-image file passed as argument");
+        }
+        requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
+        setContentView(R.layout.preview_image_activity);
+    
+        ActionBar actionBar = getSupportActionBar();
+        actionBar.setDisplayHomeAsUpEnabled(true);
+        actionBar.setTitle(mFile.getFileName());
+        actionBar.hide();
+        
+        mFullScreen = true;
+        
+        mStorageManager = new FileDataStorageManager(mAccount, getContentResolver());
+        mParentFolder = mStorageManager.getFileById(mFile.getParentId());
+        if (mParentFolder == null) {
+            // should not be necessary
+            mParentFolder = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR);
+        }
+
+        if (savedInstanceState != null) {
+            mRequestWaitingForBinder = savedInstanceState.getBoolean(KEY_WAITING_FOR_BINDER);
+        } else {
+            mRequestWaitingForBinder = false;
+        }
+        
+        createViewPager();
+
+    }
+
+    private void createViewPager() {
+        mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(), mParentFolder, mAccount, mStorageManager);
+        mViewPager = (ViewPager) findViewById(R.id.fragmentPager);
+        int position = mPreviewImagePagerAdapter.getFilePosition(mFile);
+        position = (position >= 0) ? position : 0;
+        mViewPager.setAdapter(mPreviewImagePagerAdapter); 
+        mViewPager.setOnPageChangeListener(this);
+        mViewPager.setCurrentItem(position);
+        if (position == 0 && !mFile.isDown()) {
+            // this is necessary because mViewPager.setCurrentItem(0) just after setting the adapter does not result in a call to #onPageSelected(0) 
+            mRequestWaitingForBinder = true;
+        }
+    }
+    
+    
+    @Override
+    public void onStart() {
+        super.onStart();
+        mDownloadConnection = new PreviewImageServiceConnection();
+        bindService(new Intent(this, FileDownloader.class), mDownloadConnection, Context.BIND_AUTO_CREATE);
+        mUploadConnection = new PreviewImageServiceConnection();
+        bindService(new Intent(this, FileUploader.class), mUploadConnection, Context.BIND_AUTO_CREATE);
+    }
+    
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putBoolean(KEY_WAITING_FOR_BINDER, mRequestWaitingForBinder);    
+    }
+
+
+    /** Defines callbacks for service binding, passed to bindService() */
+    private class PreviewImageServiceConnection implements ServiceConnection {
+
+        @Override
+        public void onServiceConnected(ComponentName component, IBinder service) {
+                
+            if (component.equals(new ComponentName(PreviewImageActivity.this, FileDownloader.class))) {
+                mDownloaderBinder = (FileDownloaderBinder) service;
+                if (mRequestWaitingForBinder) {
+                    mRequestWaitingForBinder = false;
+                    Log_OC.d(TAG, "Simulating reselection of current page after connection of download binder");
+                    onPageSelected(mViewPager.getCurrentItem());
+                }
+                    
+            } else if (component.equals(new ComponentName(PreviewImageActivity.this, FileUploader.class))) {
+                Log_OC.d(TAG, "Upload service connected");
+                mUploaderBinder = (FileUploaderBinder) service;
+            } else {
+                return;
+            }
+            
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName component) {
+            if (component.equals(new ComponentName(PreviewImageActivity.this, FileDownloader.class))) {
+                Log_OC.d(TAG, "Download service suddenly disconnected");
+                mDownloaderBinder = null;
+            } else if (component.equals(new ComponentName(PreviewImageActivity.this, FileUploader.class))) {
+                Log_OC.d(TAG, "Upload service suddenly disconnected");
+                mUploaderBinder = null;
+            }
+        }
+    };    
+    
+    
+    @Override
+    public void onStop() {
+        super.onStop();
+        if (mDownloadConnection != null) {
+            unbindService(mDownloadConnection);
+            mDownloadConnection = null;
+        }
+        if (mUploadConnection != null) {
+            unbindService(mUploadConnection);
+            mUploadConnection = null;
+        }
+    }
+    
+    
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+    }
+    
+    
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        boolean returnValue = false;
+        
+        switch(item.getItemId()){
+        case android.R.id.home:
+            backToDisplayActivity();
+            returnValue = true;
+            break;
+        default:
+               returnValue = super.onOptionsItemSelected(item);
+        }
+        
+        return returnValue;
+    }
+
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        //Log.e(TAG, "ACTIVITY, ONRESUME");
+        mDownloadFinishReceiver = new DownloadFinishReceiver();
+        IntentFilter filter = new IntentFilter(FileDownloader.DOWNLOAD_FINISH_MESSAGE);
+        filter.addAction(FileDownloader.DOWNLOAD_ADDED_MESSAGE);
+        registerReceiver(mDownloadFinishReceiver, filter);
+    }
+
+    @Override
+    protected void onPostResume() {
+        //Log.e(TAG, "ACTIVITY, ONPOSTRESUME");
+        super.onPostResume();
+    }
+    
+    @Override
+    public void onPause() {
+        super.onPause();
+        unregisterReceiver(mDownloadFinishReceiver);
+        mDownloadFinishReceiver = null;
+    }
+    
+
+    private void backToDisplayActivity() {
+        /*
+        Intent intent = new Intent(this, FileDisplayActivity.class);
+        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        intent.putExtra(FileDetailFragment.EXTRA_FILE, mFile);
+        intent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, mAccount);
+        startActivity(intent);
+        */
+        finish();
+    }
+    
+    
+    @Override
+    protected Dialog onCreateDialog(int id) {
+        Dialog dialog = null;
+        switch (id) {
+        case DIALOG_SHORT_WAIT: {
+            ProgressDialog working_dialog = new ProgressDialog(this);
+            working_dialog.setMessage(getResources().getString(
+                    R.string.wait_a_moment));
+            working_dialog.setIndeterminate(true);
+            working_dialog.setCancelable(false);
+            dialog = working_dialog;
+            break;
+        }
+        default:
+            dialog = null;
+        }
+        return dialog;
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onFileStateChanged() {
+        // nothing to do here!
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public FileDownloaderBinder getFileDownloaderBinder() {
+        return mDownloaderBinder;
+    }
+
+
+    @Override
+    public FileUploaderBinder getFileUploaderBinder() {
+        return mUploaderBinder;
+    }
+
+
+    @Override
+    public void showFragmentWithDetails(OCFile file) {
+        Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
+        showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
+        showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
+        showDetailsIntent.putExtra(FileDetailActivity.EXTRA_MODE, FileDetailActivity.MODE_DETAILS);
+        startActivity(showDetailsIntent);
+        int pos = mPreviewImagePagerAdapter.getFilePosition(file);
+        file = mPreviewImagePagerAdapter.getFileAt(pos);
+        
+    }
+
+    
+    private void requestForDownload(OCFile file) {
+        if (mDownloaderBinder == null) {
+            Log_OC.d(TAG, "requestForDownload called without binder to download service");
+            
+        } else if (!mDownloaderBinder.isDownloading(mAccount, file)) {
+            Intent i = new Intent(this, FileDownloader.class);
+            i.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount);
+            i.putExtra(FileDownloader.EXTRA_FILE, file);
+            startService(i);
+        }
+    }
+
+    /**
+     * This method will be invoked when a new page becomes selected. Animation is not necessarily complete.
+     * 
+     *  @param  Position        Position index of the new selected page
+     */
+    @Override
+    public void onPageSelected(int position) {
+        if (mDownloaderBinder == null) {
+            mRequestWaitingForBinder = true;
+            
+        } else {
+            OCFile currentFile = mPreviewImagePagerAdapter.getFileAt(position); 
+            getSupportActionBar().setTitle(currentFile.getFileName());
+            if (!currentFile.isDown()) {
+                if (!mPreviewImagePagerAdapter.pendingErrorAt(position)) {
+                    requestForDownload(currentFile);
+                }
+            }
+        }
+    }
+    
+    /**
+     * Called when the scroll state changes. Useful for discovering when the user begins dragging, 
+     * when the pager is automatically settling to the current page. when it is fully stopped/idle.
+     * 
+     * @param   State       The new scroll state (SCROLL_STATE_IDLE, _DRAGGING, _SETTLING
+     */
+    @Override
+    public void onPageScrollStateChanged(int state) {
+    }
+
+    /**
+     * This method will be invoked when the current page is scrolled, either as part of a programmatically 
+     * initiated smooth scroll or a user initiated touch scroll.
+     * 
+     * @param   position                Position index of the first page currently being displayed. 
+     *                                  Page position+1 will be visible if positionOffset is nonzero.
+     *                                  
+     * @param   positionOffset          Value from [0, 1) indicating the offset from the page at position.
+     * @param   positionOffsetPixels    Value in pixels indicating the offset from position. 
+     */
+    @Override
+    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+    }
+    
+
+    /**
+     * Class waiting for broadcast events from the {@link FielDownloader} service.
+     * 
+     * Updates the UI when a download is started or finished, provided that it is relevant for the
+     * folder displayed in the gallery.
+     */
+    private class DownloadFinishReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME);
+            String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
+            if (mAccount.name.equals(accountName) && 
+                    downloadedRemotePath != null) {
+
+                OCFile file = mStorageManager.getFileByPath(downloadedRemotePath);
+                int position = mPreviewImagePagerAdapter.getFilePosition(file);
+                boolean downloadWasFine = intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false);
+                //boolean isOffscreen =  Math.abs((mViewPager.getCurrentItem() - position)) <= mViewPager.getOffscreenPageLimit();
+                
+                if (position >= 0 && intent.getAction().equals(FileDownloader.DOWNLOAD_FINISH_MESSAGE)) {
+                    if (downloadWasFine) {
+                        mPreviewImagePagerAdapter.updateFile(position, file);   
+                        
+                    } else {
+                        mPreviewImagePagerAdapter.updateWithDownloadError(position);
+                    }
+                    mPreviewImagePagerAdapter.notifyDataSetChanged();   // will trigger the creation of new fragments
+                    
+                } else {
+                    Log_OC.d(TAG, "Download finished, but the fragment is offscreen");
+                }
+                
+            }
+            removeStickyBroadcast(intent);
+        }
+
+    }
+
+
+    @Override
+    public boolean onTouch(View v, MotionEvent event) {
+        if (event.getAction() == MotionEvent.ACTION_UP) {
+           toggleFullScreen();
+        }
+        return true;
+    }
+
+    
+    private void toggleFullScreen() {
+        ActionBar actionBar = getSupportActionBar();
+        if (mFullScreen) {
+            actionBar.show();
+            
+        } else {
+            actionBar.hide();
+            
+        }
+        mFullScreen = !mFullScreen;
+    }
+    
+    
+}
diff --git a/src/com/owncloud/android/ui/preview/PreviewImageFragment.java b/src/com/owncloud/android/ui/preview/PreviewImageFragment.java
new file mode 100644 (file)
index 0000000..8df06bf
--- /dev/null
@@ -0,0 +1,682 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2012-2013 ownCloud Inc. 
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License 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.ui.preview;
+
+import java.io.File;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+
+import android.accounts.Account;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapFactory.Options;
+import android.graphics.Point;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.FragmentStatePagerAdapter;
+import android.view.Display;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnTouchListener;
+import android.view.ViewGroup;
+import android.webkit.MimeTypeMap;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.actionbarsherlock.app.SherlockFragment;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.operations.OnRemoteOperationListener;
+import com.owncloud.android.operations.RemoteOperation;
+import com.owncloud.android.operations.RemoteOperationResult;
+import com.owncloud.android.operations.RemoveFileOperation;
+import com.owncloud.android.ui.fragment.ConfirmationDialogFragment;
+import com.owncloud.android.ui.fragment.FileFragment;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import eu.alefzero.webdav.WebdavUtils;
+
+
+/**
+ * This fragment shows a preview of a downloaded image.
+ * 
+ * Trying to get an instance with NULL {@link OCFile} or ownCloud {@link Account} values will produce an {@link IllegalStateException}.
+ * 
+ * If the {@link OCFile} passed is not downloaded, an {@link IllegalStateException} is generated on instantiation too.
+ * 
+ * @author David A. Velasco
+ */
+public class PreviewImageFragment extends SherlockFragment implements   FileFragment, 
+                                                                        OnRemoteOperationListener, 
+                                                                        ConfirmationDialogFragment.ConfirmationDialogFragmentListener {
+    public static final String EXTRA_FILE = "FILE";
+    public static final String EXTRA_ACCOUNT = "ACCOUNT";
+
+    private View mView;
+    private OCFile mFile;
+    private Account mAccount;
+    private FileDataStorageManager mStorageManager;
+    private ImageView mImageView;
+    private TextView mMessageView;
+    private ProgressBar mProgressWheel;
+
+    public Bitmap mBitmap = null;
+    
+    private Handler mHandler;
+    private RemoteOperation mLastRemoteOperation;
+    
+    private static final String TAG = PreviewImageFragment.class.getSimpleName();
+
+    private boolean mIgnoreFirstSavedState;
+
+    
+    /**
+     * Creates a fragment to preview an image.
+     * 
+     * When 'imageFile' or 'ocAccount' are null
+     * 
+     * @param imageFile                 An {@link OCFile} to preview as an image in the fragment
+     * @param ocAccount                 An ownCloud account; needed to start downloads
+     * @param ignoreFirstSavedState     Flag to work around an unexpected behaviour of {@link FragmentStatePagerAdapter}; TODO better solution 
+     */
+    public PreviewImageFragment(OCFile fileToDetail, Account ocAccount, boolean ignoreFirstSavedState) {
+        mFile = fileToDetail;
+        mAccount = ocAccount;
+        mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment
+        mIgnoreFirstSavedState = ignoreFirstSavedState;
+    }
+    
+    
+    /**
+     *  Creates an empty fragment for image 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 
+     */
+    public PreviewImageFragment() {
+        mFile = null;
+        mAccount = null;
+        mStorageManager = null;
+        mIgnoreFirstSavedState = false;
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mHandler = new Handler();
+        setHasOptionsMenu(true);
+    }
+    
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        super.onCreateView(inflater, container, savedInstanceState);
+        mView = inflater.inflate(R.layout.preview_image_fragment, container, false);
+        mImageView = (ImageView)mView.findViewById(R.id.image);
+        mImageView.setVisibility(View.GONE);
+        mView.setOnTouchListener((OnTouchListener)getActivity());   // WATCH OUT THAT CAST
+        mMessageView = (TextView)mView.findViewById(R.id.message);
+        mMessageView.setVisibility(View.GONE);
+        mProgressWheel = (ProgressBar)mView.findViewById(R.id.progressWheel);
+        mProgressWheel.setVisibility(View.VISIBLE);
+        return mView;
+    }
+    
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        if (!(activity instanceof FileFragment.ContainerActivity))
+            throw new ClassCastException(activity.toString() + " must implement " + FileFragment.ContainerActivity.class.getSimpleName());
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());
+        if (savedInstanceState != null) {
+            if (!mIgnoreFirstSavedState) {
+                mFile = savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_FILE);
+                mAccount = savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_ACCOUNT);
+            } else {
+                mIgnoreFirstSavedState = false;
+            }
+        }
+        if (mFile == null) {
+            throw new IllegalStateException("Instanced with a NULL OCFile");
+        }
+        if (mAccount == null) {
+            throw new IllegalStateException("Instanced with a NULL ownCloud Account");
+        }
+        if (!mFile.isDown()) {
+            throw new IllegalStateException("There is no local file to preview");
+        }
+    }
+        
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putParcelable(PreviewImageFragment.EXTRA_FILE, mFile);
+        outState.putParcelable(PreviewImageFragment.EXTRA_ACCOUNT, mAccount);
+    }
+    
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        if (mFile != null) {
+           BitmapLoader bl = new BitmapLoader(mImageView, mMessageView, mProgressWheel);
+           bl.execute(new String[]{mFile.getStoragePath()});
+        }
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        super.onCreateOptionsMenu(menu, inflater);
+
+        inflater.inflate(R.menu.file_actions_menu, menu);
+        List<Integer> toHide = new ArrayList<Integer>();    
+        
+        MenuItem item = null;
+        toHide.add(R.id.action_cancel_download);
+        toHide.add(R.id.action_cancel_upload);
+        toHide.add(R.id.action_download_file);
+        toHide.add(R.id.action_rename_file);    // by now
+
+        for (int i : toHide) {
+            item = menu.findItem(i);
+            if (item != null) {
+                item.setVisible(false);
+                item.setEnabled(false);
+            }
+        }
+        
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.action_open_file_with: {
+                openFile();
+                return true;
+            }
+            case R.id.action_remove_file: {
+                removeFile();
+                return true;
+            }
+            case R.id.action_see_details: {
+                seeDetails();
+                return true;
+            }
+            
+            default:
+                return false;
+        }
+    }
+
+    
+    private void seeDetails() {
+        ((FileFragment.ContainerActivity)getActivity()).showFragmentWithDetails(mFile);        
+    }
+
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        //Log.e(TAG, "FRAGMENT, ONRESUME");
+        /*
+        mDownloadFinishReceiver = new DownloadFinishReceiver();
+        IntentFilter filter = new IntentFilter(
+                FileDownloader.DOWNLOAD_FINISH_MESSAGE);
+        getActivity().registerReceiver(mDownloadFinishReceiver, filter);
+        
+        mUploadFinishReceiver = new UploadFinishReceiver();
+        filter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE);
+        getActivity().registerReceiver(mUploadFinishReceiver, filter);
+        */
+
+    }
+
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        /*
+        if (mVideoPreview.getVisibility() == View.VISIBLE) {
+            mSavedPlaybackPosition = mVideoPreview.getCurrentPosition();
+        }*/
+        /*
+        getActivity().unregisterReceiver(mDownloadFinishReceiver);
+        mDownloadFinishReceiver = null;
+        
+        getActivity().unregisterReceiver(mUploadFinishReceiver);
+        mUploadFinishReceiver = null;
+        */
+    }
+
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mBitmap != null) {
+            mBitmap.recycle();
+        }
+    }
+
+    
+    /**
+     * Opens the previewed image with an external application.
+     * 
+     * TODO - improve this; instead of prioritize the actions available for the MIME type in the server, 
+     * we should get a list of available apps for MIME tpye in the server and join it with the list of 
+     * available apps for the MIME type known from the file extension, to let the user choose
+     */
+    private void openFile() {
+        String storagePath = mFile.getStoragePath();
+        String encodedStoragePath = WebdavUtils.encodePath(storagePath);
+        try {
+            Intent i = new Intent(Intent.ACTION_VIEW);
+            i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mFile.getMimetype());
+            i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+            startActivity(i);
+            
+        } catch (Throwable t) {
+            Log_OC.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + mFile.getMimetype());
+            boolean toastIt = true; 
+            String mimeType = "";
+            try {
+                Intent i = new Intent(Intent.ACTION_VIEW);
+                mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1));
+                if (mimeType == null || !mimeType.equals(mFile.getMimetype())) {
+                    if (mimeType != null) {
+                        i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mimeType);
+                    } else {
+                        // desperate try
+                        i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), "*-/*");
+                    }
+                    i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+                    startActivity(i);
+                    toastIt = false;
+                }
+                
+            } catch (IndexOutOfBoundsException e) {
+                Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + storagePath);
+                
+            } catch (ActivityNotFoundException e) {
+                Log_OC.e(TAG, "No activity found to handle: " + storagePath + " with MIME type " + mimeType + " obtained from extension");
+                
+            } catch (Throwable th) {
+                Log_OC.e(TAG, "Unexpected problem when opening: " + storagePath, th);
+                
+            } finally {
+                if (toastIt) {
+                    Toast.makeText(getActivity(), "There is no application to handle file " + mFile.getFileName(), Toast.LENGTH_SHORT).show();
+                }
+            }
+            
+        }
+        finish();
+    }
+    
+    
+    /**
+     * Starts a the removal of the previewed file.
+     * 
+     * Shows a confirmation dialog. The action continues in {@link #onConfirmation(String)} , {@link #onNeutral(String)} or {@link #onCancel(String)},
+     * depending upon the user selection in the dialog. 
+     */
+    private void removeFile() {
+        ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
+                R.string.confirmation_remove_alert,
+                new String[]{mFile.getFileName()},
+                R.string.confirmation_remove_remote_and_local,
+                R.string.confirmation_remove_local,
+                R.string.common_cancel);
+        confDialog.setOnConfirmationListener(this);
+        confDialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION);
+    }
+
+    
+    /**
+     * Performs the removal of the previewed file, both locally and in the server.
+     */
+    @Override
+    public void onConfirmation(String callerTag) {
+        if (mStorageManager.getFileById(mFile.getFileId()) != null) {   // check that the file is still there;
+            mLastRemoteOperation = new RemoveFileOperation( mFile,      // TODO we need to review the interface with RemoteOperations, and use OCFile IDs instead of OCFile objects as parameters
+                                                            true, 
+                                                            mStorageManager);
+            mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
+            
+            getActivity().showDialog(PreviewImageActivity.DIALOG_SHORT_WAIT);
+        }
+    }
+    
+    
+    /**
+     * Removes the file from local storage
+     */
+    @Override
+    public void onNeutral(String callerTag) {
+        // TODO this code should be made in a secondary thread,
+        if (mFile.isDown()) {   // checks it is still there
+            File f = new File(mFile.getStoragePath());
+            f.delete();
+            mFile.setStoragePath(null);
+            mStorageManager.saveFile(mFile);
+            finish();
+        }
+    }
+    
+    /**
+     * User cancelled the removal action.
+     */
+    @Override
+    public void onCancel(String callerTag) {
+        // nothing to do here
+    }
+    
+
+    /**
+     * {@inheritDoc}
+     */
+    public OCFile getFile(){
+        return mFile;
+    }
+    
+    /*
+    /**
+     * Use this method to signal this Activity that it shall update its view.
+     * 
+     * @param file : An {@link OCFile}
+     *-/
+    public void updateFileDetails(OCFile file, Account ocAccount) {
+        mFile = file;
+        if (ocAccount != null && ( 
+                mStorageManager == null || 
+                (mAccount != null && !mAccount.equals(ocAccount))
+           )) {
+            mStorageManager = new FileDataStorageManager(ocAccount, getActivity().getApplicationContext().getContentResolver());
+        }
+        mAccount = ocAccount;
+        updateFileDetails(false);
+    }
+    */
+    
+
+    private class BitmapLoader extends AsyncTask<String, Void, Bitmap> {
+
+        /**
+         * Weak reference to the target {@link ImageView} where the bitmap will be loaded into.
+         * 
+         * Using a weak reference will avoid memory leaks if the target ImageView is retired from memory before the load finishes.
+         */
+        private final WeakReference<ImageView> mImageViewRef;
+
+        /**
+         * Weak reference to the target {@link TextView} where error messages will be written.
+         * 
+         * Using a weak reference will avoid memory leaks if the target ImageView is retired from memory before the load finishes.
+         */
+        private final WeakReference<TextView> mMessageViewRef;
+
+        
+        /**
+         * Weak reference to the target {@link Progressbar} shown while the load is in progress.
+         * 
+         * Using a weak reference will avoid memory leaks if the target ImageView is retired from memory before the load finishes.
+         */
+        private final WeakReference<ProgressBar> mProgressWheelRef;
+
+        
+        /**
+         * Error message to show when a load fails 
+         */
+        private int mErrorMessageId;
+        
+        
+        /**
+         * Constructor.
+         * 
+         * @param imageView     Target {@link ImageView} where the bitmap will be loaded into.
+         */
+        public BitmapLoader(ImageView imageView, TextView messageView, ProgressBar progressWheel) {
+            mImageViewRef = new WeakReference<ImageView>(imageView);
+            mMessageViewRef = new WeakReference<TextView>(messageView);
+            mProgressWheelRef = new WeakReference<ProgressBar>(progressWheel);
+        }
+        
+        
+        @SuppressWarnings("deprecation")
+        @SuppressLint({ "NewApi", "NewApi", "NewApi" }) // to avoid Lint errors since Android SDK r20
+               @Override
+        protected Bitmap doInBackground(String... params) {
+            Bitmap result = null;
+            if (params.length != 1) return result;
+            String storagePath = params[0];
+            try {
+                // set desired options that will affect the size of the bitmap
+                BitmapFactory.Options options = new Options();
+                options.inScaled = true;
+                options.inPurgeable = true;
+                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
+                    options.inPreferQualityOverSpeed = false;
+                }
+                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
+                    options.inMutable = false;
+                }
+                // make a false load of the bitmap - just to be able to read outWidth, outHeight and outMimeType
+                options.inJustDecodeBounds = true;
+                BitmapFactory.decodeFile(storagePath, options);   
+                
+                int width = options.outWidth;
+                int height = options.outHeight;
+                int scale = 1;
+                
+                Display display = getActivity().getWindowManager().getDefaultDisplay();
+                Point size = new Point();
+                int screenWidth;
+                int screenHeight;
+                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR2) {
+                    display.getSize(size);
+                    screenWidth = size.x;
+                    screenHeight = size.y;
+                } else {
+                    screenWidth = display.getWidth();
+                    screenHeight = display.getHeight();
+                }
+
+                if (width > screenWidth) {
+                    // second try to scale down the image , this time depending upon the screen size 
+                    scale = (int) Math.floor((float)width / screenWidth);
+                }
+                if (height > screenHeight) {
+                    scale = Math.max(scale, (int) Math.floor((float)height / screenHeight));
+                }
+                options.inSampleSize = scale;
+
+                // really load the bitmap
+                options.inJustDecodeBounds = false; // the next decodeFile call will be real
+                result = BitmapFactory.decodeFile(storagePath, options);
+                //Log_OC.d(TAG, "Image loaded - width: " + options.outWidth + ", loaded height: " + options.outHeight);
+
+                if (result == null) {
+                    mErrorMessageId = R.string.preview_image_error_unknown_format;
+                    Log_OC.e(TAG, "File could not be loaded as a bitmap: " + storagePath);
+                }
+                
+            } catch (OutOfMemoryError e) {
+                mErrorMessageId = R.string.preview_image_error_unknown_format;
+                Log_OC.e(TAG, "Out of memory occured for file " + storagePath, e);
+                    
+            } catch (NoSuchFieldError e) {
+                mErrorMessageId = R.string.common_error_unknown;
+                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 " + mFile.getStoragePath(), t);
+                
+            }
+            return result;
+        }
+        
+        @Override
+        protected void onPostExecute(Bitmap result) {
+            hideProgressWheel();
+            if (result != null) {
+                showLoadedImage(result);
+            } else {
+                showErrorMessage();
+            }
+        }
+        
+        private void showLoadedImage(Bitmap result) {
+            if (mImageViewRef != null) {
+                final ImageView imageView = mImageViewRef.get();
+                if (imageView != null) {
+                    imageView.setImageBitmap(result);
+                    imageView.setVisibility(View.VISIBLE);
+                    mBitmap  = result;
+                } // else , silently finish, the fragment was destroyed
+            }
+            if (mMessageViewRef != null) {
+                final TextView messageView = mMessageViewRef.get();
+                if (messageView != null) {
+                    messageView.setVisibility(View.GONE);
+                } // else , silently finish, the fragment was destroyed
+            }
+        }
+        
+        private void showErrorMessage() {
+            if (mImageViewRef != null) {
+                final ImageView imageView = mImageViewRef.get();
+                if (imageView != null) {
+                    // shows the default error icon
+                    imageView.setVisibility(View.VISIBLE);
+                } // else , silently finish, the fragment was destroyed
+            }
+            if (mMessageViewRef != null) {
+                final TextView messageView = mMessageViewRef.get();
+                if (messageView != null) {
+                    messageView.setText(mErrorMessageId);
+                    messageView.setVisibility(View.VISIBLE);
+                } // else , silently finish, the fragment was destroyed
+            }
+        }
+        
+        private void hideProgressWheel() {
+            if (mProgressWheelRef != null) {
+                final ProgressBar progressWheel = mProgressWheelRef.get();
+                if (progressWheel != null) {
+                    progressWheel.setVisibility(View.GONE);
+                }
+            }
+        }
+        
+    }
+
+    /**
+     * Helper method to test if an {@link OCFile} can be passed to a {@link PreviewImageFragment} 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.isImage());
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
+        if (operation.equals(mLastRemoteOperation) && operation instanceof RemoveFileOperation) {
+            onRemoveFileOperationFinish((RemoveFileOperation)operation, result);
+        }
+    }
+    
+    private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) {
+        getActivity().dismissDialog(PreviewImageActivity.DIALOG_SHORT_WAIT);
+        
+        if (result.isSuccess()) {
+            Toast msg = Toast.makeText(getActivity().getApplicationContext(), R.string.remove_success_msg, Toast.LENGTH_LONG);
+            msg.show();
+            finish();
+                
+        } else {
+            Toast msg = Toast.makeText(getActivity(), R.string.remove_fail_msg, Toast.LENGTH_LONG); 
+            msg.show();
+            if (result.isSslRecoverableException()) {
+                // TODO show the SSL warning dialog
+            }
+        }
+    }
+
+    /**
+     * Finishes the preview
+     */
+    private void finish() {
+        Activity container = getActivity();
+        container.finish();
+    }
+    
+    
+}
diff --git a/src/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java b/src/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java
new file mode 100644 (file)
index 0000000..a253624
--- /dev/null
@@ -0,0 +1,344 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2012-2013  ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License 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.ui.preview;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+
+import android.accounts.Account;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentStatePagerAdapter;
+import android.view.ViewGroup;
+
+import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.ui.fragment.FileFragment;
+
+/**
+ * Adapter class that provides Fragment instances  
+ * 
+ * @author David A. Velasco
+ */
+//public class PreviewImagePagerAdapter extends PagerAdapter {
+public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter {
+    
+    private static final String TAG = PreviewImagePagerAdapter.class.getSimpleName();
+            
+    private Vector<OCFile> mImageFiles;
+    private Account mAccount;
+    private Set<Object> mObsoleteFragments;
+    private Set<Integer> mObsoletePositions;
+    private Set<Integer> mDownloadErrors;
+    private DataStorageManager mStorageManager;
+    
+    private Map<Integer, FileFragment> mCachedFragments;
+
+    /*
+    private final FragmentManager mFragmentManager;
+    private FragmentTransaction mCurTransaction = null;
+    private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();
+    private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
+    private Fragment mCurrentPrimaryItem = null;
+    */
+
+    /**
+     * Constructor.
+     * 
+     * @param fragmentManager   {@link FragmentManager} instance that will handle the {@link Fragment}s provided by the adapter. 
+     * @param parentFolder      Folder where images will be searched for.
+     * @param storageManager    Bridge to database.
+     */
+    public PreviewImagePagerAdapter(FragmentManager fragmentManager, OCFile parentFolder, Account account, DataStorageManager storageManager) {
+        super(fragmentManager);
+        
+        if (fragmentManager == null) {
+            throw new IllegalArgumentException("NULL FragmentManager instance");
+        }
+        if (parentFolder == null) {
+            throw new IllegalArgumentException("NULL parent folder");
+        } 
+        if (storageManager == null) {
+            throw new IllegalArgumentException("NULL storage manager");
+        }
+
+        mAccount = account;
+        mStorageManager = storageManager;
+        mImageFiles = mStorageManager.getDirectoryImages(parentFolder); 
+        mObsoleteFragments = new HashSet<Object>();
+        mObsoletePositions = new HashSet<Integer>();
+        mDownloadErrors = new HashSet<Integer>();
+        //mFragmentManager = fragmentManager;
+        mCachedFragments = new HashMap<Integer, FileFragment>();
+    }
+
+    
+    /**
+     * Returns the image files handled by the adapter.
+     * 
+     * @return  A vector with the image files handled by the adapter.
+     */
+    protected OCFile getFileAt(int position) {
+        return mImageFiles.get(position);
+    }
+
+    
+    public Fragment getItem(int i) {
+        OCFile file = mImageFiles.get(i);
+        Fragment fragment = null;
+        if (file.isDown()) {
+            fragment = new PreviewImageFragment(file, mAccount, mObsoletePositions.contains(Integer.valueOf(i)));
+            
+        } else if (mDownloadErrors.contains(Integer.valueOf(i))) {
+            fragment = new FileDownloadFragment(file, mAccount, true);
+            ((FileDownloadFragment)fragment).setError(true);
+            mDownloadErrors.remove(Integer.valueOf(i));
+            
+        } else {
+            fragment = new FileDownloadFragment(file, mAccount, mObsoletePositions.contains(Integer.valueOf(i)));
+        }
+        mObsoletePositions.remove(Integer.valueOf(i));
+        return fragment;
+    }
+
+    public int getFilePosition(OCFile file) {
+        return mImageFiles.indexOf(file);
+    }
+    
+    @Override
+    public int getCount() {
+        return mImageFiles.size();
+    }
+
+    @Override
+    public CharSequence getPageTitle(int position) {
+        return mImageFiles.get(position).getFileName();
+    }
+
+    
+    public void updateFile(int position, OCFile file) {
+        FileFragment fragmentToUpdate = mCachedFragments.get(Integer.valueOf(position));
+        if (fragmentToUpdate != null) {
+            mObsoleteFragments.add(fragmentToUpdate);
+        }
+        mObsoletePositions.add(Integer.valueOf(position));
+        mImageFiles.set(position, file);
+    }
+    
+    
+    public void updateWithDownloadError(int position) {
+        FileFragment fragmentToUpdate = mCachedFragments.get(Integer.valueOf(position));
+        if (fragmentToUpdate != null) {
+            mObsoleteFragments.add(fragmentToUpdate);
+        }
+        mDownloadErrors.add(Integer.valueOf(position));
+    }
+    
+    public void clearErrorAt(int position) {
+        FileFragment fragmentToUpdate = mCachedFragments.get(Integer.valueOf(position));
+        if (fragmentToUpdate != null) {
+            mObsoleteFragments.add(fragmentToUpdate);
+        }
+        mDownloadErrors.remove(Integer.valueOf(position));
+    }
+    
+    
+    @Override
+    public int getItemPosition(Object object) {
+        if (mObsoleteFragments.contains(object)) {
+            mObsoleteFragments.remove(object);
+            return POSITION_NONE;
+        }
+        return super.getItemPosition(object);
+    }
+
+
+    @Override
+    public Object instantiateItem(ViewGroup container, int position) {
+        Object fragment = super.instantiateItem(container, position);
+        mCachedFragments.put(Integer.valueOf(position), (FileFragment)fragment);
+        return fragment;
+    }
+    
+    @Override
+    public void destroyItem(ViewGroup container, int position, Object object) {
+       mCachedFragments.remove(Integer.valueOf(position));
+       super.destroyItem(container, position, object);
+    }
+
+
+    public boolean pendingErrorAt(int position) {
+        return mDownloadErrors.contains(Integer.valueOf(position));
+    }
+
+
+    
+    /* -*
+     * Called when a change in the shown pages is going to start being made.
+     * 
+     * @param   container   The containing View which is displaying this adapter's page views.
+     *- /
+    @Override
+    public void startUpdate(ViewGroup container) {
+        Log.e(TAG, "** startUpdate");
+    }
+
+    @Override
+    public Object instantiateItem(ViewGroup container, int position) {
+        Log.e(TAG, "** instantiateItem " + position);
+        
+        if (mFragments.size() > position) {
+            Fragment fragment = mFragments.get(position);
+            if (fragment != null) {
+                Log.e(TAG, "** \t returning cached item");
+                return fragment;
+            }
+        }
+
+        if (mCurTransaction == null) {
+            mCurTransaction = mFragmentManager.beginTransaction();
+        }
+
+        Fragment fragment = getItem(position);
+        if (mSavedState.size() > position) {
+            Fragment.SavedState savedState = mSavedState.get(position);
+            if (savedState != null) {
+                // TODO WATCH OUT:
+                // * The Fragment must currently be attached to the FragmentManager.
+                // * A new Fragment created using this saved state must be the same class type as the Fragment it was created from.
+                // * The saved state can not contain dependencies on other fragments -- that is it can't use putFragment(Bundle, String, Fragment) 
+                //   to store a fragment reference                 
+                fragment.setInitialSavedState(savedState);
+            }
+        }
+        while (mFragments.size() <= position) {
+            mFragments.add(null);
+        }
+        fragment.setMenuVisibility(false);
+        mFragments.set(position, fragment);
+        //Log.e(TAG, "** \t adding fragment at position " + position + ", containerId " + container.getId());
+        mCurTransaction.add(container.getId(), fragment);
+
+        return fragment;
+    }
+
+    @Override
+    public void destroyItem(ViewGroup container, int position, Object object) {
+        Log.e(TAG, "** destroyItem " + position);
+        Fragment fragment = (Fragment)object;
+        
+        if (mCurTransaction == null) {
+            mCurTransaction = mFragmentManager.beginTransaction();
+        }
+        Log.e(TAG, "** \t removing fragment at position " + position);
+        while (mSavedState.size() <= position) {
+            mSavedState.add(null);
+        }
+        mSavedState.set(position, mFragmentManager.saveFragmentInstanceState(fragment));
+        mFragments.set(position, null);
+
+        mCurTransaction.remove(fragment);
+    }
+
+    @Override
+    public void setPrimaryItem(ViewGroup container, int position, Object object) {
+        Fragment fragment = (Fragment)object;
+        if (fragment != mCurrentPrimaryItem) {
+            if (mCurrentPrimaryItem != null) {
+                mCurrentPrimaryItem.setMenuVisibility(false);
+            }
+            if (fragment != null) {
+                fragment.setMenuVisibility(true);
+            }
+            mCurrentPrimaryItem = fragment;
+        }
+    }
+
+    @Override
+    public void finishUpdate(ViewGroup container) {
+        Log.e(TAG, "** finishUpdate (start)");
+        if (mCurTransaction != null) {
+            mCurTransaction.commitAllowingStateLoss();
+            mCurTransaction = null;
+            mFragmentManager.executePendingTransactions();
+        }
+        Log.e(TAG, "** finishUpdate (end)");
+    }
+
+    @Override
+    public boolean isViewFromObject(View view, Object object) {
+        return ((Fragment)object).getView() == view;
+    }
+
+    @Override
+    public Parcelable saveState() {
+        Bundle state = null;
+        if (mSavedState.size() > 0) {
+            state = new Bundle();
+            Fragment.SavedState[] savedStates = new Fragment.SavedState[mSavedState.size()];
+            mSavedState.toArray(savedStates);
+            state.putParcelableArray("states", savedStates);
+        }
+        for (int i=0; i<mFragments.size(); i++) {
+            Fragment fragment = mFragments.get(i);
+            if (fragment != null) {
+                if (state == null) {
+                    state = new Bundle();
+                }
+                String key = "f" + i;
+                mFragmentManager.putFragment(state, key, fragment);
+            }
+        }
+        return state;
+    }
+
+    @Override
+    public void restoreState(Parcelable state, ClassLoader loader) {
+        if (state != null) {
+            Bundle bundle = (Bundle)state;
+            bundle.setClassLoader(loader);
+            Parcelable[] states = bundle.getParcelableArray("states");
+            mSavedState.clear();
+            mFragments.clear();
+            if (states != null) {
+                for (int i=0; i<states.length; i++) {
+                    mSavedState.add((Fragment.SavedState)states[i]);
+                }
+            }
+            Iterable<String> keys = bundle.keySet();
+            for (String key: keys) {
+                if (key.startsWith("f")) {
+                    int index = Integer.parseInt(key.substring(1));
+                    Fragment f = mFragmentManager.getFragment(bundle, key);
+                    if (f != null) {
+                        while (mFragments.size() <= index) {
+                            mFragments.add(null);
+                        }
+                        f.setMenuVisibility(false);
+                        mFragments.set(index, f);
+                    } else {
+                        Log.w(TAG, "Bad fragment at key " + key);
+                    }
+                }
+            }
+        }
+    }
+    */
+}
diff --git a/src/com/owncloud/android/ui/preview/PreviewMediaFragment.java b/src/com/owncloud/android/ui/preview/PreviewMediaFragment.java
new file mode 100644 (file)
index 0000000..c26d8c7
--- /dev/null
@@ -0,0 +1,752 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2012-2013 ownCloud Inc. 
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License 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.ui.preview;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.accounts.Account;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnCompletionListener;
+import android.media.MediaPlayer.OnErrorListener;
+import android.media.MediaPlayer.OnPreparedListener;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.support.v4.app.FragmentTransaction;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+import android.view.ViewGroup;
+import android.webkit.MimeTypeMap;
+import android.widget.ImageView;
+import android.widget.Toast;
+import android.widget.VideoView;
+
+import com.actionbarsherlock.app.SherlockFragment;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.media.MediaControlView;
+import com.owncloud.android.media.MediaService;
+import com.owncloud.android.media.MediaServiceBinder;
+import com.owncloud.android.operations.OnRemoteOperationListener;
+import com.owncloud.android.operations.RemoteOperation;
+import com.owncloud.android.operations.RemoteOperationResult;
+import com.owncloud.android.operations.RemoveFileOperation;
+import com.owncloud.android.ui.activity.FileDetailActivity;
+import com.owncloud.android.ui.activity.FileDisplayActivity;
+import com.owncloud.android.ui.fragment.ConfirmationDialogFragment;
+import com.owncloud.android.ui.fragment.FileDetailFragment;
+import com.owncloud.android.ui.fragment.FileFragment;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import eu.alefzero.webdav.WebdavUtils;
+
+/**
+ * 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}.
+ * 
+ * By now, if the {@link OCFile} passed is not downloaded, an {@link IllegalStateException} is generated on instantiation too.
+ * 
+ * @author David A. Velasco
+ */
+public class PreviewMediaFragment extends SherlockFragment implements
+        OnTouchListener , FileFragment,  
+        ConfirmationDialogFragment.ConfirmationDialogFragmentListener, OnRemoteOperationListener  {
+
+    public static final String EXTRA_FILE = "FILE";
+    public static final String EXTRA_ACCOUNT = "ACCOUNT";
+    private static final String EXTRA_PLAY_POSITION = "PLAY_POSITION";
+    private static final String EXTRA_PLAYING = "PLAYING";
+
+    private View mView;
+    private OCFile mFile;
+    private Account mAccount;
+    private FileDataStorageManager mStorageManager;
+    private ImageView mImagePreview;
+    private VideoView mVideoPreview;
+    private int mSavedPlaybackPosition;
+    
+    private Handler mHandler;
+    private RemoteOperation mLastRemoteOperation;
+    
+    private MediaServiceBinder mMediaServiceBinder = null;
+    private MediaControlView mMediaController = null;
+    private MediaServiceConnection mMediaServiceConnection = null;
+    private VideoHelper mVideoHelper;
+    private boolean mAutoplay;
+    
+    private static final String TAG = PreviewMediaFragment.class.getSimpleName();
+
+    
+    /**
+     * Creates a fragment to preview a file.
+     * 
+     * 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
+     */
+    public PreviewMediaFragment(OCFile fileToDetail, Account ocAccount) {
+        mFile = fileToDetail;
+        mAccount = ocAccount;
+        mSavedPlaybackPosition = 0;
+        mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment 
+        mAutoplay = true;
+    }
+    
+    
+    /**
+     *  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 
+     */
+    public PreviewMediaFragment() {
+        mFile = null;
+        mAccount = null;
+        mSavedPlaybackPosition = 0;
+        mStorageManager = null;
+        mAutoplay = true;
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mHandler = new Handler();
+        setHasOptionsMenu(true);
+    }
+    
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        super.onCreateView(inflater, container, savedInstanceState);
+        
+        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);
+        mVideoPreview.setOnTouchListener(this);
+        
+        mMediaController = (MediaControlView)mView.findViewById(R.id.media_controller);
+        
+        return mView;
+    }
+    
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        if (!(activity instanceof FileFragment.ContainerActivity))
+            throw new ClassCastException(activity.toString() + " must implement " + FileFragment.ContainerActivity.class.getSimpleName());
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+
+        mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());
+        if (savedInstanceState != null) {
+            mFile = savedInstanceState.getParcelable(PreviewMediaFragment.EXTRA_FILE);
+            mAccount = savedInstanceState.getParcelable(PreviewMediaFragment.EXTRA_ACCOUNT);
+            mSavedPlaybackPosition = savedInstanceState.getInt(PreviewMediaFragment.EXTRA_PLAY_POSITION);
+            mAutoplay = savedInstanceState.getBoolean(PreviewMediaFragment.EXTRA_PLAYING);
+            
+        }
+        if (mFile == null) {
+            throw new IllegalStateException("Instanced with a NULL OCFile");
+        }
+        if (mAccount == null) {
+            throw new IllegalStateException("Instanced with a NULL ownCloud Account");
+        }
+        if (!mFile.isDown()) {
+            throw new IllegalStateException("There is no local file to preview");
+        }
+        if (mFile.isVideo()) {
+            mVideoPreview.setVisibility(View.VISIBLE);
+            mImagePreview.setVisibility(View.GONE);
+            prepareVideo();
+            
+        } else {
+            mVideoPreview.setVisibility(View.GONE);
+            mImagePreview.setVisibility(View.VISIBLE);
+        }
+        
+    }
+        
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putParcelable(PreviewMediaFragment.EXTRA_FILE, mFile);
+        outState.putParcelable(PreviewMediaFragment.EXTRA_ACCOUNT, mAccount);
+        
+        if (mFile.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 , mMediaServiceBinder.getCurrentPosition());
+            outState.putBoolean(PreviewMediaFragment.EXTRA_PLAYING , mMediaServiceBinder.isPlaying());
+        }
+    }
+    
+
+    @Override
+    public void onStart() {
+        super.onStart();
+
+        if (mFile != null) {
+           if (mFile.isAudio()) {
+               bindMediaService();
+               
+           } else if (mFile.isVideo()) {
+               stopAudio();
+               playVideo(); 
+           }
+        }
+    }
+    
+    
+    private void stopAudio() {
+        Intent i = new Intent(getSherlockActivity(), MediaService.class);
+        i.setAction(MediaService.ACTION_STOP_ALL);
+        getSherlockActivity().startService(i);
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        super.onCreateOptionsMenu(menu, inflater);
+
+        inflater.inflate(R.menu.file_actions_menu, menu);
+        List<Integer> toHide = new ArrayList<Integer>();    
+        
+        MenuItem item = null;
+        toHide.add(R.id.action_cancel_download);
+        toHide.add(R.id.action_cancel_upload);
+        toHide.add(R.id.action_download_file);
+        toHide.add(R.id.action_rename_file);    // by now
+
+        for (int i : toHide) {
+            item = menu.findItem(i);
+            if (item != null) {
+                item.setVisible(false);
+                item.setEnabled(false);
+            }
+        }
+        
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.action_open_file_with: {
+                openFile();
+                return true;
+            }
+            case R.id.action_remove_file: {
+                removeFile();
+                return true;
+            }
+            case R.id.action_see_details: {
+                seeDetails();
+                return true;
+            }
+            
+            default:
+                return false;
+        }
+    }
+
+    
+    private void seeDetails() {
+        stopPreview(false);
+        ((FileFragment.ContainerActivity)getActivity()).showFragmentWithDetails(mFile);        
+    }
+
+
+    private void prepareVideo() {
+        // create helper to get more control on the playback
+        mVideoHelper = new VideoHelper();
+        mVideoPreview.setOnPreparedListener(mVideoHelper);
+        mVideoPreview.setOnCompletionListener(mVideoHelper);
+        mVideoPreview.setOnErrorListener(mVideoHelper);
+    }
+    
+    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
+        mVideoPreview.setVideoPath(mFile.getStoragePath()); 
+    }
+    
+
+    private class VideoHelper implements OnCompletionListener, OnPreparedListener, OnErrorListener {
+        
+        /** 
+         * Called when the file is ready to be played.
+         * 
+         * Just starts the playback.
+         * 
+         * @param   mp    {@link MediaPlayer} instance performing the playback.
+         */
+        @Override
+        public void onPrepared(MediaPlayer vp) {
+            Log_OC.e(TAG, "onPrepared");
+            mVideoPreview.seekTo(mSavedPlaybackPosition);
+            if (mAutoplay) { 
+                mVideoPreview.start();
+            }
+            mMediaController.setEnabled(true);
+            mMediaController.updatePausePlay();
+        }
+        
+        
+        /**
+         * Called when the file is finished playing.
+         *  
+         * Finishes the activity.
+         * 
+         * @param   mp    {@link MediaPlayer} instance performing the playback.
+         */
+        @Override
+        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(mFile.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
+         */
+        @Override
+        public boolean onError(MediaPlayer mp, int what, int extra) {
+            if (mVideoPreview.getWindowToken() != null) {
+                String message = MediaService.getMessageForMediaError(getActivity(), what, extra);
+                new AlertDialog.Builder(getActivity())
+                        .setMessage(message)
+                        .setPositiveButton(android.R.string.VideoView_error_button,
+                                new DialogInterface.OnClickListener() {
+                                    public void onClick(DialogInterface dialog, int whichButton) {
+                                        dialog.dismiss();
+                                        VideoHelper.this.onCompletion(null);
+                                    }
+                                })
+                        .setCancelable(false)
+                        .show();
+            }
+            return true;
+        }
+        
+    }
+
+    
+    @Override
+    public void onStop() {
+        super.onStop();
+        
+        if (mMediaServiceConnection != null) {
+            Log_OC.d(TAG, "Unbinding from MediaService ...");
+            if (mMediaServiceBinder != null && mMediaController != null) {
+                mMediaServiceBinder.unregisterMediaController(mMediaController);
+            }
+            getActivity().unbindService(mMediaServiceConnection);
+            mMediaServiceConnection = null;
+            mMediaServiceBinder = null;
+        }
+    }
+    
+    @Override
+    public boolean onTouch(View v, MotionEvent event) {
+        if (event.getAction() == MotionEvent.ACTION_DOWN && v == mVideoPreview) {
+            startFullScreenVideo();
+            return true;        
+        }
+        return false;
+    }
+
+    
+    private void startFullScreenVideo() {
+        Intent i = new Intent(getActivity(), PreviewVideoActivity.class);
+        i.putExtra(PreviewVideoActivity.EXTRA_ACCOUNT, mAccount);
+        i.putExtra(PreviewVideoActivity.EXTRA_FILE, mFile);
+        i.putExtra(PreviewVideoActivity.EXTRA_AUTOPLAY, mVideoPreview.isPlaying());
+        mVideoPreview.pause();
+        i.putExtra(PreviewVideoActivity.EXTRA_START_POSITION, mVideoPreview.getCurrentPosition());
+        startActivityForResult(i, 0);
+    }
+
+    
+    @Override
+    public void onActivityResult (int requestCode, int resultCode, Intent data) {
+        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); 
+        }
+    }
+    
+
+    private void playAudio() {
+        if (!mMediaServiceBinder.isPlaying(mFile)) {
+            Log_OC.d(TAG, "starting playback of " + mFile.getStoragePath());
+            mMediaServiceBinder.start(mAccount, mFile, mAutoplay, mSavedPlaybackPosition);
+            
+        } else {
+            if (!mMediaServiceBinder.isPlaying() && mAutoplay) {
+                mMediaServiceBinder.start();
+                mMediaController.updatePausePlay();
+            }
+        }
+    }
+
+
+    private void bindMediaService() {
+        Log_OC.d(TAG, "Binding to MediaService...");
+        if (mMediaServiceConnection == null) {
+            mMediaServiceConnection = new MediaServiceConnection();
+        }
+        getActivity().bindService(  new Intent(getActivity(), 
+                                    MediaService.class),
+                                    mMediaServiceConnection, 
+                                    Context.BIND_AUTO_CREATE);
+            // follow the flow in MediaServiceConnection#onServiceConnected(...)
+    }
+    
+    /** Defines callbacks for service binding, passed to bindService() */
+    private class MediaServiceConnection implements ServiceConnection {
+
+        @Override
+        public void onServiceConnected(ComponentName component, IBinder service) {
+            if (component.equals(new ComponentName(getActivity(), MediaService.class))) {
+                Log_OC.d(TAG, "Media service connected");
+                mMediaServiceBinder = (MediaServiceBinder) service;
+                if (mMediaServiceBinder != null) {
+                    prepareMediaController();
+                    playAudio();    // do not wait for the touch of nobody to play audio
+                    
+                    Log_OC.d(TAG, "Successfully bound to MediaService, MediaController ready");
+                    
+                } else {
+                    Log_OC.e(TAG, "Unexpected response from MediaService while binding");
+                }
+            }
+        }
+
+        private void prepareMediaController() {
+            mMediaServiceBinder.registerMediaController(mMediaController);
+            if (mMediaController != null) {
+                mMediaController.setMediaPlayer(mMediaServiceBinder);
+                mMediaController.setEnabled(true);
+                mMediaController.updatePausePlay();
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName component) {
+            if (component.equals(new ComponentName(getActivity(), MediaService.class))) {
+                Log_OC.e(TAG, "Media service suddenly disconnected");
+                if (mMediaController != null) {
+                    mMediaController.setMediaPlayer(null);
+                } else {
+                    Toast.makeText(getActivity(), "No media controller to release when disconnected from media service", Toast.LENGTH_SHORT).show();
+                }
+                mMediaServiceBinder = null;
+                mMediaServiceConnection = null;
+            }
+        }
+    }    
+
+    
+
+    /**
+     * Opens the previewed file with an external application.
+     * 
+     * TODO - improve this; instead of prioritize the actions available for the MIME type in the server, 
+     * we should get a list of available apps for MIME tpye in the server and join it with the list of 
+     * available apps for the MIME type known from the file extension, to let the user choose
+     */
+    private void openFile() {
+        stopPreview(true);
+        String storagePath = mFile.getStoragePath();
+        String encodedStoragePath = WebdavUtils.encodePath(storagePath);
+        try {
+            Intent i = new Intent(Intent.ACTION_VIEW);
+            i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mFile.getMimetype());
+            i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+            startActivity(i);
+            
+        } catch (Throwable t) {
+            Log_OC.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + mFile.getMimetype());
+            boolean toastIt = true; 
+            String mimeType = "";
+            try {
+                Intent i = new Intent(Intent.ACTION_VIEW);
+                mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1));
+                if (mimeType == null || !mimeType.equals(mFile.getMimetype())) {
+                    if (mimeType != null) {
+                        i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mimeType);
+                    } else {
+                        // desperate try
+                        i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), "*-/*");
+                    }
+                    i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+                    startActivity(i);
+                    toastIt = false;
+                }
+                
+            } catch (IndexOutOfBoundsException e) {
+                Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + storagePath);
+                
+            } catch (ActivityNotFoundException e) {
+                Log_OC.e(TAG, "No activity found to handle: " + storagePath + " with MIME type " + mimeType + " obtained from extension");
+                
+            } catch (Throwable th) {
+                Log_OC.e(TAG, "Unexpected problem when opening: " + storagePath, th);
+                
+            } finally {
+                if (toastIt) {
+                    Toast.makeText(getActivity(), "There is no application to handle file " + mFile.getFileName(), Toast.LENGTH_SHORT).show();
+                }
+            }
+            
+        }
+        finish();
+    }
+    
+    /**
+     * Starts a the removal of the previewed file.
+     * 
+     * Shows a confirmation dialog. The action continues in {@link #onConfirmation(String)} , {@link #onNeutral(String)} or {@link #onCancel(String)},
+     * depending upon the user selection in the dialog. 
+     */
+    private void removeFile() {
+        ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
+                R.string.confirmation_remove_alert,
+                new String[]{mFile.getFileName()},
+                R.string.confirmation_remove_remote_and_local,
+                R.string.confirmation_remove_local,
+                R.string.common_cancel);
+        confDialog.setOnConfirmationListener(this);
+        confDialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION);
+    }
+
+    
+    /**
+     * Performs the removal of the previewed file, both locally and in the server.
+     */
+    @Override
+    public void onConfirmation(String callerTag) {
+        if (mStorageManager.getFileById(mFile.getFileId()) != null) {   // check that the file is still there;
+            stopPreview(true);
+            mLastRemoteOperation = new RemoveFileOperation( mFile,      // TODO we need to review the interface with RemoteOperations, and use OCFile IDs instead of OCFile objects as parameters
+                                                            true, 
+                                                            mStorageManager);
+            mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
+            
+            boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;
+            getActivity().showDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);
+        }
+    }
+    
+    
+    /**
+     * Removes the file from local storage
+     */
+    @Override
+    public void onNeutral(String callerTag) {
+        // TODO this code should be made in a secondary thread,
+        if (mFile.isDown()) {   // checks it is still there
+            stopPreview(true);
+            File f = new File(mFile.getStoragePath());
+            f.delete();
+            mFile.setStoragePath(null);
+            mStorageManager.saveFile(mFile);
+            finish();
+        }
+    }
+    
+    /**
+     * User cancelled the removal action.
+     */
+    @Override
+    public void onCancel(String callerTag) {
+        // nothing to do here
+    }
+    
+
+    /**
+     * {@inheritDoc}
+     */
+    public OCFile getFile(){
+        return mFile;
+    }
+    
+    /*
+    /**
+     * Use this method to signal this Activity that it shall update its view.
+     * 
+     * @param file : An {@link OCFile}
+     *-/
+    public void updateFileDetails(OCFile file, Account ocAccount) {
+        mFile = file;
+        if (ocAccount != null && ( 
+                mStorageManager == null || 
+                (mAccount != null && !mAccount.equals(ocAccount))
+           )) {
+            mStorageManager = new FileDataStorageManager(ocAccount, getActivity().getApplicationContext().getContentResolver());
+        }
+        mAccount = ocAccount;
+        updateFileDetails(false);
+    }
+    */
+    
+
+    /**
+     * 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.
+     */
+    public static boolean canBePreviewed(OCFile file) {
+        return (file != null && (file.isAudio() || file.isVideo()));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
+        if (operation.equals(mLastRemoteOperation)) {
+            if (operation instanceof RemoveFileOperation) {
+                onRemoveFileOperationFinish((RemoveFileOperation)operation, result);
+            }
+        }
+    }
+    
+    private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) {
+        boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;
+        getActivity().dismissDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);
+        
+        if (result.isSuccess()) {
+            Toast msg = Toast.makeText(getActivity().getApplicationContext(), R.string.remove_success_msg, Toast.LENGTH_LONG);
+            msg.show();
+            finish();
+                
+        } else {
+            Toast msg = Toast.makeText(getActivity(), R.string.remove_fail_msg, Toast.LENGTH_LONG); 
+            msg.show();
+            if (result.isSslRecoverableException()) {
+                // TODO show the SSL warning dialog
+            }
+        }
+    }
+
+    private void stopPreview(boolean stopAudio) {
+        if (mFile.isAudio() && stopAudio) {
+            mMediaServiceBinder.pause();
+            
+        } else if (mFile.isVideo()) {
+            mVideoPreview.stopPlayback();
+        }
+    }
+
+
+
+    /**
+     * Finishes the preview
+     */
+    private void finish() {
+        Activity container = getActivity();
+        if (container instanceof FileDisplayActivity) {
+            // double pane
+            FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction();
+            transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null), FileDetailFragment.FTAG); // empty FileDetailFragment
+            transaction.commit();
+            ((FileFragment.ContainerActivity)container).onFileStateChanged();
+        } else {
+            container.finish();
+        }
+    }
+    
+}
diff --git a/src/com/owncloud/android/ui/preview/PreviewVideoActivity.java b/src/com/owncloud/android/ui/preview/PreviewVideoActivity.java
new file mode 100644 (file)
index 0000000..3cbff7d
--- /dev/null
@@ -0,0 +1,282 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License 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.ui.preview;
+
+import android.accounts.Account;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnCompletionListener;
+import android.media.MediaPlayer.OnErrorListener;
+import android.media.MediaPlayer.OnPreparedListener;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.widget.MediaController;
+import android.widget.VideoView;
+
+import com.owncloud.android.AccountUtils;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.media.MediaService;
+
+/**
+ *  Activity implementing a basic video player.
+ * 
+ *  Used as an utility to preview video files contained in an ownCloud account.
+ *  
+ *  Currently, it always plays in landscape mode, full screen. When the playback ends,
+ *  the activity is finished. 
+ *  
+ *  @author David A. Velasco
+ */
+public class PreviewVideoActivity extends Activity implements OnCompletionListener, OnPreparedListener, OnErrorListener {
+
+    /** Key to receive an {@link OCFile} to play as an extra value in an {@link Intent} */
+    public static final String EXTRA_FILE = "FILE";
+    
+    /** Key to receive the ownCloud {@link Account} where the file to play is saved as an extra value in an {@link Intent} */
+    public static final String EXTRA_ACCOUNT = "ACCOUNT";
+    
+    /** Key to receive a flag signaling if the video should be started immediately */
+    public static final String EXTRA_AUTOPLAY = "AUTOPLAY";
+    
+    /** Key to receive the position of the playback where the video should be put at start */
+    public static final String EXTRA_START_POSITION = "START_POSITION";
+    
+    private static final String TAG = PreviewVideoActivity.class.getSimpleName();
+
+    private OCFile mFile;                       // video file to play
+    private Account mAccount;                   // ownCloud account holding mFile
+    private int mSavedPlaybackPosition;         // in the unit time handled by MediaPlayer.getCurrentPosition()
+    private boolean mAutoplay;                  // when 'true', the playback starts immediately with the activity
+    private VideoView mVideoPlayer;             // view to play the file; both performs and show the playback
+    private MediaController mMediaController;   // panel control used by the user to control the playback
+          
+    /** 
+     *  Called when the activity is first created.
+     *  
+     *  Searches for an {@link OCFile} and ownCloud {@link Account} holding it in the starting {@link Intent}.
+     *  
+     *  The {@link Account} is unnecessary if the file is downloaded; else, the {@link Account} is used to 
+     *  try to stream the remote file - TODO get the streaming works
+     * 
+     *  {@inheritDoc}
+     */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log_OC.e(TAG, "ACTIVITY\t\tonCreate");
+        
+        setContentView(R.layout.video_layout);
+    
+        if (savedInstanceState == null) {
+            Bundle extras = getIntent().getExtras();
+            mFile = extras.getParcelable(EXTRA_FILE);
+            mAccount = extras.getParcelable(EXTRA_ACCOUNT);
+            mSavedPlaybackPosition = extras.getInt(EXTRA_START_POSITION);
+            mAutoplay = extras.getBoolean(EXTRA_AUTOPLAY);
+            
+        } else {
+            mFile = savedInstanceState.getParcelable(EXTRA_FILE);
+            mAccount = savedInstanceState.getParcelable(EXTRA_ACCOUNT);
+            mSavedPlaybackPosition = savedInstanceState.getInt(EXTRA_START_POSITION);
+            mAutoplay = savedInstanceState.getBoolean(EXTRA_AUTOPLAY);
+        }
+          
+        mVideoPlayer = (VideoView) findViewById(R.id.videoPlayer);
+
+        // set listeners to get more contol on the playback
+        mVideoPlayer.setOnPreparedListener(this);
+        mVideoPlayer.setOnCompletionListener(this);
+        mVideoPlayer.setOnErrorListener(this);
+          
+        // keep the screen on while the playback is performed (prevents screen off by battery save)
+        mVideoPlayer.setKeepScreenOn(true);
+        
+        if (mFile != null) {
+            if (mFile.isDown()) {
+                mVideoPlayer.setVideoPath(mFile.getStoragePath());
+                
+            } else if (mAccount != null) {
+                // not working now
+                String url = AccountUtils.constructFullURLForAccount(this, mAccount) + mFile.getRemotePath();
+                mVideoPlayer.setVideoURI(Uri.parse(url));
+                
+            } else {
+                onError(null, MediaService.OC_MEDIA_ERROR, R.string.media_err_no_account);
+            }
+            
+            // create and prepare control panel for the user
+            mMediaController = new MediaController(this);
+            mMediaController.setMediaPlayer(mVideoPlayer);
+            mMediaController.setAnchorView(mVideoPlayer);
+            mVideoPlayer.setMediaController(mMediaController);
+            
+        } else {
+            onError(null, MediaService.OC_MEDIA_ERROR, R.string.media_err_nothing_to_play);
+        }
+    }    
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        Log_OC.e(TAG, "ACTIVITY\t\tonSaveInstanceState");
+        outState.putParcelable(PreviewVideoActivity.EXTRA_FILE, mFile);
+        outState.putParcelable(PreviewVideoActivity.EXTRA_ACCOUNT, mAccount);
+        outState.putInt(PreviewVideoActivity.EXTRA_START_POSITION, mVideoPlayer.getCurrentPosition());
+        outState.putBoolean(PreviewVideoActivity.EXTRA_AUTOPLAY , mVideoPlayer.isPlaying());
+    }
+
+    
+    @Override
+    public void onBackPressed() {
+        Log_OC.e(TAG, "ACTIVTIY\t\tonBackPressed");
+        Intent i = new Intent();
+        i.putExtra(EXTRA_AUTOPLAY, mVideoPlayer.isPlaying());
+        i.putExtra(EXTRA_START_POSITION, mVideoPlayer.getCurrentPosition());
+        setResult(RESULT_OK, i);
+        super.onBackPressed();
+    }
+
+    
+    @Override
+    public void onResume() {
+        super.onResume();
+        Log_OC.e(TAG, "ACTIVTIY\t\tonResume");
+    }
+
+    
+    @Override
+    public void onStart() {
+        super.onStart();
+        Log_OC.e(TAG, "ACTIVTIY\t\tonStart");
+    }
+    
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        Log_OC.e(TAG, "ACTIVITY\t\tonDestroy");
+    }
+    
+    @Override
+    public void onStop() {
+        super.onStop();
+        Log_OC.e(TAG, "ACTIVTIY\t\tonStop");
+    }
+    
+    
+    @Override
+    public void onPause() {
+        super.onPause();
+        Log_OC.e(TAG, "ACTIVTIY\t\tonPause");
+    }
+    
+    
+    /** 
+     * Called when the file is ready to be played.
+     * 
+     * Just starts the playback.
+     * 
+     * @param   mp    {@link MediaPlayer} instance performing the playback.
+     */
+    @Override
+    public void onPrepared(MediaPlayer mp) {
+        Log_OC.e(TAG, "ACTIVITY\t\tonPrepare");
+        mVideoPlayer.seekTo(mSavedPlaybackPosition);
+        if (mAutoplay) { 
+            mVideoPlayer.start();
+        }
+        mMediaController.show(5000);  
+    }
+    
+    
+    /**
+     * Called when the file is finished playing.
+     *  
+     * Rewinds the video
+     * 
+     * @param   mp    {@link MediaPlayer} instance performing the playback.
+     */
+    @Override
+    public void onCompletion(MediaPlayer  mp) {
+        mVideoPlayer.seekTo(0);
+    }
+    
+    
+    /**
+     * 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
+     */
+    @Override
+    public boolean onError(MediaPlayer mp, int what, int extra) {
+        Log_OC.e(TAG, "Error in video playback, what = " + what + ", extra = " + extra);
+        
+        if (mMediaController != null) {
+            mMediaController.hide();
+        }
+        
+        if (mVideoPlayer.getWindowToken() != null) {
+            String message = MediaService.getMessageForMediaError(this, what, extra);
+            new AlertDialog.Builder(this)
+                    .setMessage(message)
+                    .setPositiveButton(android.R.string.VideoView_error_button,
+                            new DialogInterface.OnClickListener() {
+                                public void onClick(DialogInterface dialog, int whichButton) {
+                                    PreviewVideoActivity.this.onCompletion(null);
+                                }
+                            })
+                    .setCancelable(false)
+                    .show();
+        }
+        return true;
+    }
+    
+    
+    /**  
+     * Screen touches trigger the appearance of the control panel for a limited time.
+     *
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean onTouchEvent (MotionEvent ev){ 
+        /*if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            if (mMediaController.isShowing()) {
+                mMediaController.hide();
+            } else {
+                mMediaController.show(MediaService.MEDIA_CONTROL_SHORT_LIFE);
+            }
+            return true;        
+        } else {
+            return false;
+        }*/
+        return false;
+    }
+
+
+}
\ No newline at end of file
index 5406cb5..dda7b5a 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -20,46 +19,59 @@ package com.owncloud.android.utils;
 
 import java.io.File;
 
+import android.annotation.SuppressLint;
+import android.content.Context;
 import android.net.Uri;
 import android.os.Environment;
 import android.os.StatFs;
 
+import com.owncloud.android.R;
 import com.owncloud.android.datamodel.OCFile;
 
-
 /**
  * Static methods to help in access to local file system.
  * 
  * @author David A. Velasco
  */
 public class FileStorageUtils {
-    
+    private static final String LOG_TAG = "FileStorageUtils";
+
     public static final String getSavePath(String accountName) {
         File sdCard = Environment.getExternalStorageDirectory();
-        return sdCard.getAbsolutePath() + "/owncloud/" + Uri.encode(accountName, "@");   
-            // URL encoding is an 'easy fix' to overcome that NTFS and FAT32 don't allow ":" in file names, that can be in the accountName since 0.1.190B
+        return sdCard.getAbsolutePath() + "/owncloud/" + Uri.encode(accountName, "@");
+        // URL encoding is an 'easy fix' to overcome that NTFS and FAT32 don't allow ":" in file names, that can be in the accountName since 0.1.190B
     }
-    
+
     public static final String getDefaultSavePathFor(String accountName, OCFile file) {
         return getSavePath(accountName) + file.getRemotePath();
     }
-    
+
     public static final String getTemporalPath(String accountName) {
         File sdCard = Environment.getExternalStorageDirectory();
         return sdCard.getAbsolutePath() + "/owncloud/tmp/" + Uri.encode(accountName, "@");
             // URL encoding is an 'easy fix' to overcome that NTFS and FAT32 don't allow ":" in file names, that can be in the accountName since 0.1.190B
     }
 
+    @SuppressLint("NewApi")
     public static final long getUsableSpace(String accountName) {
         File savePath = Environment.getExternalStorageDirectory();
         if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD) {
             return savePath.getUsableSpace();
-            
+
         } else {
             StatFs stats = new StatFs(savePath.getAbsolutePath());
             return stats.getAvailableBlocks() * stats.getBlockSize();
         }
-        
+
     }
     
+    public static final String getLogPath()  {
+        return Environment.getExternalStorageDirectory() + File.separator + "owncloud" + File.separator + "log";
+    }
+
+    public static String getInstantUploadFilePath(Context context, String fileName) {
+        String uploadPath = context.getString(R.string.instant_upload_path);
+        String value = uploadPath + OCFile.PATH_SEPARATOR +  (fileName == null ? "" : fileName);
+        return value;
+    }
 }
\ No newline at end of file
index 8bba93f..630e7ac 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index 3ce8853..be44f8f 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index d77a825..efa6655 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index d39f8ce..3f45396 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -30,9 +29,10 @@ import java.util.Set;
 
 import org.apache.commons.httpclient.methods.RequestEntity;
 
-import eu.alefzero.webdav.OnDatatransferProgressListener;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.network.ProgressiveDataTransferer;
 
-import android.util.Log;
+import eu.alefzero.webdav.OnDatatransferProgressListener;
 
 
 /**
@@ -40,7 +40,7 @@ import android.util.Log;
  * 
  * @author David A. Velasco
  */
-public class ChunkFromFileChannelRequestEntity implements RequestEntity {
+public class ChunkFromFileChannelRequestEntity implements RequestEntity, ProgressiveDataTransferer {
 
     private static final String TAG = ChunkFromFileChannelRequestEntity.class.getSimpleName();
     
@@ -90,16 +90,25 @@ public class ChunkFromFileChannelRequestEntity implements RequestEntity {
         return true;
     }
     
-    public void addOnDatatransferProgressListener(OnDatatransferProgressListener listener) {
-        mDataTransferListeners.add(listener);
+    @Override
+    public void addDatatransferProgressListener(OnDatatransferProgressListener listener) {
+        synchronized (mDataTransferListeners) {
+            mDataTransferListeners.add(listener);
+        }
     }
     
-    public void addOnDatatransferProgressListeners(Collection<OnDatatransferProgressListener> listeners) {
-        mDataTransferListeners.addAll(listeners);
+    @Override
+    public void addDatatransferProgressListeners(Collection<OnDatatransferProgressListener> listeners) {
+        synchronized (mDataTransferListeners) {
+            mDataTransferListeners.addAll(listeners);
+        }
     }
     
-    public void removeOnDatatransferProgressListener(OnDatatransferProgressListener listener) {
-        mDataTransferListeners.remove(listener);
+    @Override
+    public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) {
+        synchronized (mDataTransferListeners) {
+            mDataTransferListeners.remove(listener);
+        }
     }
     
     
@@ -116,14 +125,16 @@ public class ChunkFromFileChannelRequestEntity implements RequestEntity {
                 out.write(mBuffer.array(), 0, readCount);
                 mBuffer.clear();
                 mTransferred += readCount;
-                it = mDataTransferListeners.iterator();
-                while (it.hasNext()) {
-                    it.next().onTransferProgress(readCount, mTransferred, size, mFile.getName());
+                synchronized (mDataTransferListeners) {
+                    it = mDataTransferListeners.iterator();
+                    while (it.hasNext()) {
+                        it.next().onTransferProgress(readCount, mTransferred, size, mFile.getName());
+                    }
                 }
             }
             
         } catch (IOException io) {
-            Log.e(TAG, io.getMessage());
+            Log_OC.e(TAG, io.getMessage());
             throw new RuntimeException("Ugly solution to workaround the default policy of retries when the server falls while uploading ; temporal fix; really", io);   
             
         }
index 3adaa3a..a8f01d6 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -32,16 +31,17 @@ import java.util.Set;
 
 import org.apache.commons.httpclient.methods.RequestEntity;
 
-import eu.alefzero.webdav.OnDatatransferProgressListener;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.network.ProgressiveDataTransferer;
 
-import android.util.Log;
+import eu.alefzero.webdav.OnDatatransferProgressListener;
 
 
 /**
  * A RequestEntity that represents a File.
  * 
  */
-public class FileRequestEntity implements RequestEntity {
+public class FileRequestEntity implements RequestEntity, ProgressiveDataTransferer {
 
     final File mFile;
     final String mContentType;
@@ -70,17 +70,26 @@ public class FileRequestEntity implements RequestEntity {
     public boolean isRepeatable() {
         return true;
     }
-    
-    public void addOnDatatransferProgressListener(OnDatatransferProgressListener listener) {
-        mDataTransferListeners.add(listener);
+
+    @Override
+    public void addDatatransferProgressListener(OnDatatransferProgressListener listener) {
+        synchronized (mDataTransferListeners) {
+            mDataTransferListeners.add(listener);
+        }
     }
     
-    public void addOnDatatransferProgressListeners(Collection<OnDatatransferProgressListener> listeners) {
-        mDataTransferListeners.addAll(listeners);
+    @Override
+    public void addDatatransferProgressListeners(Collection<OnDatatransferProgressListener> listeners) {
+        synchronized (mDataTransferListeners) {
+            mDataTransferListeners.addAll(listeners);
+        }
     }
     
-    public void removeOnDatatransferProgressListener(OnDatatransferProgressListener listener) {
-        mDataTransferListeners.remove(listener);
+    @Override
+    public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) {
+        synchronized (mDataTransferListeners) {
+            mDataTransferListeners.remove(listener);
+        }
     }
     
     
@@ -103,14 +112,16 @@ public class FileRequestEntity implements RequestEntity {
                 out.write(tmp.array(), 0, readResult);
                 tmp.clear();
                 transferred += readResult;
-                it = mDataTransferListeners.iterator();
-                while (it.hasNext()) {
-                    it.next().onTransferProgress(readResult, transferred, size, mFile.getName());
+                synchronized (mDataTransferListeners) {
+                    it = mDataTransferListeners.iterator();
+                    while (it.hasNext()) {
+                        it.next().onTransferProgress(readResult, transferred, size, mFile.getName());
+                    }
                 }
             }
             
         } catch (IOException io) {
-            Log.e("FileRequestException", io.getMessage());
+            Log_OC.e("FileRequestException", io.getMessage());
             throw new RuntimeException("Ugly solution to workaround the default policy of retries when the server falls while uploading ; temporal fix; really", io);   
             
         } finally {
index a8c84b1..5c4783a 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index 7577152..0b3cf81 100644 (file)
-/* ownCloud Android client application\r
- *   Copyright (C) 2011  Bartek Przybylski\r
- *   Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- *   This program is free software: you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation, either version 2 of the License, or\r
- *   (at your option) any later version.\r
- *\r
- *   This program is distributed in the hope that it will be useful,\r
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- *   GNU General Public License for more details.\r
- *\r
- *   You should have received a copy of the GNU General Public License\r
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-package eu.alefzero.webdav;\r
-\r
-import java.io.BufferedInputStream;\r
-import java.io.File;\r
-import java.io.FileOutputStream;\r
-import java.io.IOException;\r
-import java.io.InputStream;\r
-import java.util.ArrayList;\r
-import java.util.List;\r
-\r
-import org.apache.commons.httpclient.Credentials;\r
-import org.apache.commons.httpclient.HostConfiguration;\r
-import org.apache.commons.httpclient.HttpClient;\r
-import org.apache.commons.httpclient.HttpConnectionManager;\r
-import org.apache.commons.httpclient.HttpException;\r
-import org.apache.commons.httpclient.HttpMethod;\r
-import org.apache.commons.httpclient.HttpMethodBase;\r
-import org.apache.commons.httpclient.HttpState;\r
-import org.apache.commons.httpclient.HttpVersion;\r
-import org.apache.commons.httpclient.UsernamePasswordCredentials;\r
-import org.apache.commons.httpclient.auth.AuthPolicy;\r
-import org.apache.commons.httpclient.auth.AuthScope;\r
-import org.apache.commons.httpclient.methods.GetMethod;\r
-import org.apache.commons.httpclient.methods.HeadMethod;\r
-import org.apache.commons.httpclient.methods.PutMethod;\r
-import org.apache.commons.httpclient.params.HttpMethodParams;\r
-import org.apache.http.HttpStatus;\r
-import org.apache.http.params.CoreProtocolPNames;\r
-import org.apache.jackrabbit.webdav.client.methods.DavMethod;\r
-import org.apache.jackrabbit.webdav.client.methods.DeleteMethod;\r
-\r
-import com.owncloud.android.network.BearerAuthScheme;\r
-import com.owncloud.android.network.BearerCredentials;\r
-\r
-import android.net.Uri;\r
-import android.util.Log;\r
-\r
-public class WebdavClient extends HttpClient {\r
-    private Uri mUri;\r
-    private Credentials mCredentials;\r
-    final private static String TAG = "WebdavClient";\r
-    private static final String USER_AGENT = "Android-ownCloud";\r
-    \r
-    private OnDatatransferProgressListener mDataTransferListener;\r
-    static private byte[] sExhaustBuffer = new byte[1024];\r
-    \r
-    /**\r
-     * Constructor\r
-     */\r
-    public WebdavClient(HttpConnectionManager connectionMgr) {\r
-        super(connectionMgr);\r
-        Log.d(TAG, "Creating WebdavClient");\r
-        getParams().setParameter(HttpMethodParams.USER_AGENT, USER_AGENT);\r
-        getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);\r
-    }\r
-\r
-    public void setBearerCredentials(String accessToken) {\r
-        AuthPolicy.registerAuthScheme(BearerAuthScheme.AUTH_POLICY, BearerAuthScheme.class);\r
-        \r
-        List<String> authPrefs = new ArrayList<String>(1);\r
-        authPrefs.add(BearerAuthScheme.AUTH_POLICY);\r
-        getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs);        \r
-        \r
-        mCredentials = new BearerCredentials(accessToken);\r
-        getState().setCredentials(AuthScope.ANY, mCredentials);\r
-    }\r
-\r
-    public void setBasicCredentials(String username, String password) {\r
-        List<String> authPrefs = new ArrayList<String>(1);\r
-        authPrefs.add(AuthPolicy.BASIC);\r
-        getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs);        \r
-        \r
-        getParams().setAuthenticationPreemptive(true);\r
-        mCredentials = new UsernamePasswordCredentials(username, password);\r
-        getState().setCredentials(AuthScope.ANY, mCredentials);\r
-    }\r
-    \r
-    \r
-    /**\r
-     * Downloads a file in remoteFilepath to the local targetPath.\r
-     * \r
-     * @param remoteFilepath    Path to the file in the remote server, URL DECODED. \r
-     * @param targetFile        Local path to save the downloaded file.\r
-     * @return                  'True' when the file is successfully downloaded.\r
-     */\r
-    public boolean downloadFile(String remoteFilePath, File targetFile) {\r
-        boolean ret = false;\r
-        GetMethod get = new GetMethod(mUri.toString() + WebdavUtils.encodePath(remoteFilePath));\r
-\r
-        try {\r
-            int status = executeMethod(get);\r
-            if (status == HttpStatus.SC_OK) {\r
-                targetFile.createNewFile();\r
-                BufferedInputStream bis = new BufferedInputStream(\r
-                        get.getResponseBodyAsStream());\r
-                FileOutputStream fos = new FileOutputStream(targetFile);\r
-\r
-                byte[] bytes = new byte[4096];\r
-                int readResult;\r
-                while ((readResult = bis.read(bytes)) != -1) {\r
-                    if (mDataTransferListener != null)\r
-                        mDataTransferListener.onTransferProgress(readResult);\r
-                    fos.write(bytes, 0, readResult);\r
-                }\r
-                fos.close();\r
-                ret = true;\r
-            } else {\r
-                exhaustResponse(get.getResponseBodyAsStream());\r
-            }\r
-            Log.e(TAG, "Download of " + remoteFilePath + " to " + targetFile + " finished with HTTP status " + status + (!ret?"(FAIL)":""));\r
-        } catch (Exception e) {\r
-            logException(e, "dowloading " + remoteFilePath);\r
-            \r
-        } finally {\r
-            if (!ret && targetFile.exists()) {\r
-                targetFile.delete();\r
-            }\r
-            get.releaseConnection();    // let the connection available for other methods\r
-        }\r
-        return ret;\r
-    }\r
-    \r
-    /**\r
-     * Deletes a remote file via webdav\r
-     * @param remoteFilePath       Remote file path of the file to delete, in URL DECODED format.\r
-     * @return\r
-     */\r
-    public boolean deleteFile(String remoteFilePath) {\r
-        boolean ret = false;\r
-        DavMethod delete = new DeleteMethod(mUri.toString() + WebdavUtils.encodePath(remoteFilePath));\r
-        try {\r
-            int status = executeMethod(delete);\r
-            ret = (status == HttpStatus.SC_OK || status == HttpStatus.SC_ACCEPTED || status == HttpStatus.SC_NO_CONTENT);\r
-            exhaustResponse(delete.getResponseBodyAsStream());\r
-            \r
-            Log.e(TAG, "DELETE of " + remoteFilePath + " finished with HTTP status " + status +  (!ret?"(FAIL)":""));\r
-            \r
-        } catch (Exception e) {\r
-            logException(e, "deleting " + remoteFilePath);\r
-            \r
-        } finally {\r
-            delete.releaseConnection();    // let the connection available for other methods\r
-        }\r
-        return ret;\r
-    }\r
-\r
-    \r
-    public void setDataTransferProgressListener(OnDatatransferProgressListener listener) {\r
-        mDataTransferListener = listener;\r
-    }\r
-    \r
-    /**\r
-     * Creates or update a file in the remote server with the contents of a local file.\r
-     * \r
-     * @param localFile         Path to the local file to upload.\r
-     * @param remoteTarget      Remote path to the file to create or update, URL DECODED\r
-     * @param contentType       MIME type of the file.\r
-     * @return                  Status HTTP code returned by the server.\r
-     * @throws IOException      When a transport error that could not be recovered occurred while uploading the file to the server.\r
-     * @throws HttpException    When a violation of the HTTP protocol occurred. \r
-     */\r
-    public int putFile(String localFile, String remoteTarget, String contentType) throws HttpException, IOException {\r
-        int status = -1;\r
-        PutMethod put = new PutMethod(mUri.toString() + WebdavUtils.encodePath(remoteTarget));\r
-        \r
-        try {\r
-            File f = new File(localFile);\r
-            FileRequestEntity entity = new FileRequestEntity(f, contentType);\r
-            entity.addOnDatatransferProgressListener(mDataTransferListener);\r
-            put.setRequestEntity(entity);\r
-            status = executeMethod(put);\r
-            \r
-            exhaustResponse(put.getResponseBodyAsStream());\r
-            \r
-        } finally {\r
-            put.releaseConnection();    // let the connection available for other methods\r
-        }\r
-        return status;\r
-    }\r
-    \r
-    /**\r
-     * Tries to log in to the current URI, with the current credentials\r
-     * \r
-     * @return A {@link HttpStatus}-Code of the result. SC_OK is good.\r
-     */\r
-    public int tryToLogin() {\r
-        int status = 0;\r
-        HeadMethod head = new HeadMethod(mUri.toString());\r
-        try {\r
-            status = executeMethod(head);\r
-            boolean result = status == HttpStatus.SC_OK;\r
-            Log.d(TAG, "HEAD for " + mUri + " finished with HTTP status " + status + (!result?"(FAIL)":""));\r
-            exhaustResponse(head.getResponseBodyAsStream());\r
-            \r
-        } catch (Exception e) {\r
-            logException(e, "trying to login at " + mUri.toString());\r
-            \r
-        } finally {\r
-            head.releaseConnection();\r
-        }\r
-        return status;\r
-    }\r
-\r
-    \r
-    /**\r
-     * Check if a file exists in the OC server\r
-     * \r
-     * @return              'true' if the file exists; 'false' it doesn't exist\r
-     * @throws  Exception   When the existence could not be determined\r
-     */\r
-    public boolean existsFile(String path) throws IOException, HttpException {\r
-        HeadMethod head = new HeadMethod(mUri.toString() + WebdavUtils.encodePath(path));\r
-        try {\r
-            int status = executeMethod(head);\r
-            Log.d(TAG, "HEAD to " + path + " finished with HTTP status " + status + ((status != HttpStatus.SC_OK)?"(FAIL)":""));\r
-            exhaustResponse(head.getResponseBodyAsStream());\r
-            return (status == HttpStatus.SC_OK);\r
-            \r
-        } finally {\r
-            head.releaseConnection();    // let the connection available for other methods\r
-        }\r
-    }\r
-\r
-\r
-    /**\r
-     * Requests the received method with the received timeout (milliseconds).\r
-     * \r
-     * Executes the method through the inherited HttpClient.executedMethod(method).\r
-     * \r
-     * Sets the socket and connection timeouts only for the method received.\r
-     * \r
-     * The timeouts are both in milliseconds; 0 means 'infinite'; < 0 means 'do not change the default'\r
-     * \r
-     * @param method            HTTP method request.\r
-     * @param readTimeout       Timeout to set for data reception\r
-     * @param conntionTimout    Timeout to set for connection establishment\r
-     */\r
-    public int executeMethod(HttpMethodBase method, int readTimeout, int connectionTimeout) throws HttpException, IOException {\r
-        int oldSoTimeout = getParams().getSoTimeout();\r
-        int oldConnectionTimeout = getHttpConnectionManager().getParams().getConnectionTimeout();\r
-        try {\r
-            if (readTimeout >= 0) { \r
-                method.getParams().setSoTimeout(readTimeout);   // this should be enough...\r
-                getParams().setSoTimeout(readTimeout);          // ... but this looks like necessary for HTTPS\r
-            }\r
-            if (connectionTimeout >= 0) {\r
-                getHttpConnectionManager().getParams().setConnectionTimeout(connectionTimeout);\r
-            }\r
-            return executeMethod(method);\r
-        } finally {\r
-            getParams().setSoTimeout(oldSoTimeout);\r
-            getHttpConnectionManager().getParams().setConnectionTimeout(oldConnectionTimeout);\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Exhausts a not interesting HTTP response. Encouraged by HttpClient documentation.\r
-     * \r
-     * @param responseBodyAsStream      InputStream with the HTTP response to exhaust.\r
-     */\r
-    public void exhaustResponse(InputStream responseBodyAsStream) {\r
-        if (responseBodyAsStream != null) {\r
-            try {\r
-                while (responseBodyAsStream.read(sExhaustBuffer) >= 0);\r
-                responseBodyAsStream.close();\r
-            \r
-            } catch (IOException io) {\r
-                Log.e(TAG, "Unexpected exception while exhausting not interesting HTTP response; will be IGNORED", io);\r
-            }\r
-        }\r
-    }\r
-\r
-\r
-    /**\r
-     * Logs an exception triggered in a HTTP request. \r
-     * \r
-     * @param e         Caught exception.\r
-     * @param doing     Suffix to add at the end of the logged message.\r
-     */\r
-    private void logException(Exception e, String doing) {\r
-        if (e instanceof HttpException) {\r
-            Log.e(TAG, "HTTP violation while " + doing, e);\r
-\r
-        } else if (e instanceof IOException) {\r
-            Log.e(TAG, "Unrecovered transport exception while " + doing, e);\r
-\r
-        } else {\r
-            Log.e(TAG, "Unexpected exception while " + doing, e);\r
-        }\r
-    }\r
-\r
-    \r
-    /**\r
-     * Sets the connection and wait-for-data timeouts to be applied by default to the methods performed by this client.\r
-     */\r
-    public void setDefaultTimeouts(int defaultDataTimeout, int defaultConnectionTimeout) {\r
-            getParams().setSoTimeout(defaultDataTimeout);\r
-            getHttpConnectionManager().getParams().setConnectionTimeout(defaultConnectionTimeout);\r
-    }\r
-\r
-    /**\r
-     * Sets the base URI for the helper methods that receive paths as parameters, instead of full URLs\r
-     * @param uri\r
-     */\r
-    public void setBaseUri(Uri uri) {\r
-        mUri = uri;\r
-    }\r
-\r
-    public Uri getBaseUri() {\r
-        return mUri;\r
-    }\r
-    \r
-\r
+/* ownCloud Android client application
+ *   Copyright (C) 2011  Bartek Przybylski
+ *   Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License 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 eu.alefzero.webdav;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.httpclient.Credentials;
+import org.apache.commons.httpclient.HostConfiguration;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpConnectionManager;
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.HttpMethod;
+import org.apache.commons.httpclient.HttpMethodBase;
+import org.apache.commons.httpclient.HttpState;
+import org.apache.commons.httpclient.HttpVersion;
+import org.apache.commons.httpclient.UsernamePasswordCredentials;
+import org.apache.commons.httpclient.auth.AuthPolicy;
+import org.apache.commons.httpclient.auth.AuthScope;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.methods.HeadMethod;
+import org.apache.commons.httpclient.methods.PutMethod;
+import org.apache.commons.httpclient.params.HttpMethodParams;
+import org.apache.http.HttpStatus;
+import org.apache.http.params.CoreProtocolPNames;
+import org.apache.jackrabbit.webdav.client.methods.DavMethod;
+import org.apache.jackrabbit.webdav.client.methods.DeleteMethod;
+
+import com.owncloud.android.Log_OC;
+
+import com.owncloud.android.network.BearerAuthScheme;
+import com.owncloud.android.network.BearerCredentials;
+
+import android.net.Uri;
+
+public class WebdavClient extends HttpClient {
+    private Uri mUri;
+    private Credentials mCredentials;
+    final private static String TAG = "WebdavClient";
+    private static final String USER_AGENT = "Android-ownCloud";
+    
+    private OnDatatransferProgressListener mDataTransferListener;
+    static private byte[] sExhaustBuffer = new byte[1024];
+    
+    /**
+     * Constructor
+     */
+    public WebdavClient(HttpConnectionManager connectionMgr) {
+        super(connectionMgr);
+        Log_OC.d(TAG, "Creating WebdavClient");
+        getParams().setParameter(HttpMethodParams.USER_AGENT, USER_AGENT);
+        getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
+    }
+
+    public void setBearerCredentials(String accessToken) {
+        AuthPolicy.registerAuthScheme(BearerAuthScheme.AUTH_POLICY, BearerAuthScheme.class);
+        
+        List<String> authPrefs = new ArrayList<String>(1);
+        authPrefs.add(BearerAuthScheme.AUTH_POLICY);
+        getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs);        
+        
+        mCredentials = new BearerCredentials(accessToken);
+        getState().setCredentials(AuthScope.ANY, mCredentials);
+    }
+
+    public void setBasicCredentials(String username, String password) {
+        List<String> authPrefs = new ArrayList<String>(1);
+        authPrefs.add(AuthPolicy.BASIC);
+        getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs);        
+        
+        getParams().setAuthenticationPreemptive(true);
+        mCredentials = new UsernamePasswordCredentials(username, password);
+        getState().setCredentials(AuthScope.ANY, mCredentials);
+    }
+    
+    /**
+     * Downloads a file in remoteFilepath to the local targetPath.
+     * 
+     * @param remoteFilepath    Path to the file in the remote server, URL DECODED. 
+     * @param targetFile        Local path to save the downloaded file.
+     * @return                  'True' when the file is successfully downloaded.
+     */
+    public boolean downloadFile(String remoteFilePath, File targetFile) {
+        boolean ret = false;
+        GetMethod get = new GetMethod(mUri.toString() + WebdavUtils.encodePath(remoteFilePath));
+
+        try {
+            int status = executeMethod(get);
+            if (status == HttpStatus.SC_OK) {
+                targetFile.createNewFile();
+                BufferedInputStream bis = new BufferedInputStream(
+                        get.getResponseBodyAsStream());
+                FileOutputStream fos = new FileOutputStream(targetFile);
+
+                byte[] bytes = new byte[4096];
+                int readResult;
+                while ((readResult = bis.read(bytes)) != -1) {
+                    if (mDataTransferListener != null)
+                        mDataTransferListener.onTransferProgress(readResult);
+                    fos.write(bytes, 0, readResult);
+                }
+                fos.close();
+                ret = true;
+            } else {
+                exhaustResponse(get.getResponseBodyAsStream());
+            }
+            Log_OC.e(TAG, "Download of " + remoteFilePath + " to " + targetFile + " finished with HTTP status " + status + (!ret?"(FAIL)":""));
+            
+        } catch (Exception e) {
+            logException(e, "dowloading " + remoteFilePath);
+            
+        } finally {
+            if (!ret && targetFile.exists()) {
+                targetFile.delete();
+            }
+            get.releaseConnection();    // let the connection available for other methods
+        }
+        return ret;
+    }
+    
+    
+    /**
+     * Deletes a remote file via webdav
+     * @param remoteFilePath       Remote file path of the file to delete, in URL DECODED format.
+     * @return
+     */
+    public boolean deleteFile(String remoteFilePath) {
+        boolean ret = false;
+        DavMethod delete = new DeleteMethod(mUri.toString() + WebdavUtils.encodePath(remoteFilePath));
+        try {
+            int status = executeMethod(delete);
+            ret = (status == HttpStatus.SC_OK || status == HttpStatus.SC_ACCEPTED || status == HttpStatus.SC_NO_CONTENT);
+            exhaustResponse(delete.getResponseBodyAsStream());
+            
+            Log_OC.e(TAG, "DELETE of " + remoteFilePath + " finished with HTTP status " + status +  (!ret?"(FAIL)":""));
+            
+        } catch (Exception e) {
+            logException(e, "deleting " + remoteFilePath);
+            
+        } finally {
+            delete.releaseConnection();    // let the connection available for other methods
+        }
+        return ret;
+    }
+
+    
+    public void setDataTransferProgressListener(OnDatatransferProgressListener listener) {
+        mDataTransferListener = listener;
+    }
+    
+    /**
+     * Creates or update a file in the remote server with the contents of a local file.
+     * 
+     * @param localFile         Path to the local file to upload.
+     * @param remoteTarget      Remote path to the file to create or update, URL DECODED
+     * @param contentType       MIME type of the file.
+     * @return                  Status HTTP code returned by the server.
+     * @throws IOException      When a transport error that could not be recovered occurred while uploading the file to the server.
+     * @throws HttpException    When a violation of the HTTP protocol occurred. 
+     */
+    public int putFile(String localFile, String remoteTarget, String contentType) throws HttpException, IOException {
+        int status = -1;
+        PutMethod put = new PutMethod(mUri.toString() + WebdavUtils.encodePath(remoteTarget));
+        
+        try {
+            File f = new File(localFile);
+            FileRequestEntity entity = new FileRequestEntity(f, contentType);
+            entity.addDatatransferProgressListener(mDataTransferListener);
+            put.setRequestEntity(entity);
+            status = executeMethod(put);
+            
+            exhaustResponse(put.getResponseBodyAsStream());
+            
+        } finally {
+            put.releaseConnection();    // let the connection available for other methods
+        }
+        return status;
+    }
+    
+    /**
+     * Tries to log in to the current URI, with the current credentials
+     * 
+     * @return A {@link HttpStatus}-Code of the result. SC_OK is good.
+     */
+    public int tryToLogin() {
+        int status = 0;
+        HeadMethod head = new HeadMethod(mUri.toString());
+        try {
+            status = executeMethod(head);
+            boolean result = status == HttpStatus.SC_OK;
+            Log_OC.d(TAG, "HEAD for " + mUri + " finished with HTTP status " + status + (!result?"(FAIL)":""));
+            exhaustResponse(head.getResponseBodyAsStream());
+            
+        } catch (Exception e) {
+            logException(e, "trying to login at " + mUri.toString());
+            
+        } finally {
+            head.releaseConnection();
+        }
+        return status;
+    }
+
+    /**
+     * Check if a file exists in the OC server
+     * 
+     * @return              'true' if the file exists; 'false' it doesn't exist
+     * @throws  Exception   When the existence could not be determined
+     */
+    public boolean existsFile(String path) throws IOException, HttpException {
+        HeadMethod head = new HeadMethod(mUri.toString() + WebdavUtils.encodePath(path));
+        try {
+            int status = executeMethod(head);
+            Log_OC.d(TAG, "HEAD to " + path + " finished with HTTP status " + status + ((status != HttpStatus.SC_OK)?"(FAIL)":""));
+            exhaustResponse(head.getResponseBodyAsStream());
+            return (status == HttpStatus.SC_OK);
+            
+        } finally {
+            head.releaseConnection();    // let the connection available for other methods
+        }
+    }
+    
+    /**
+     * Requests the received method with the received timeout (milliseconds).
+     * 
+     * Executes the method through the inherited HttpClient.executedMethod(method).
+     * 
+     * Sets the socket and connection timeouts only for the method received.
+     * 
+     * The timeouts are both in milliseconds; 0 means 'infinite'; < 0 means 'do not change the default'
+     * 
+     * @param method            HTTP method request.
+     * @param readTimeout       Timeout to set for data reception
+     * @param conntionTimout    Timeout to set for connection establishment
+     */
+    public int executeMethod(HttpMethodBase method, int readTimeout, int connectionTimeout) throws HttpException, IOException {
+        int oldSoTimeout = getParams().getSoTimeout();
+        int oldConnectionTimeout = getHttpConnectionManager().getParams().getConnectionTimeout();
+        try {
+            if (readTimeout >= 0) { 
+                method.getParams().setSoTimeout(readTimeout);   // this should be enough...
+                getParams().setSoTimeout(readTimeout);          // ... but this looks like necessary for HTTPS
+            }
+            if (connectionTimeout >= 0) {
+                getHttpConnectionManager().getParams().setConnectionTimeout(connectionTimeout);
+            }
+            return executeMethod(method);
+        } finally {
+            getParams().setSoTimeout(oldSoTimeout);
+            getHttpConnectionManager().getParams().setConnectionTimeout(oldConnectionTimeout);
+        }
+    }
+
+    /**
+     * Exhausts a not interesting HTTP response. Encouraged by HttpClient documentation.
+     * 
+     * @param responseBodyAsStream      InputStream with the HTTP response to exhaust.
+     */
+    public void exhaustResponse(InputStream responseBodyAsStream) {
+        if (responseBodyAsStream != null) {
+            try {
+                while (responseBodyAsStream.read(sExhaustBuffer) >= 0);
+                responseBodyAsStream.close();
+            
+            } catch (IOException io) {
+                Log_OC.e(TAG, "Unexpected exception while exhausting not interesting HTTP response; will be IGNORED", io);
+            }
+        }
+    }
+
+    /**
+     * Logs an exception triggered in a HTTP request. 
+     * 
+     * @param e         Caught exception.
+     * @param doing     Suffix to add at the end of the logged message.
+     */
+    private void logException(Exception e, String doing) {
+        if (e instanceof HttpException) {
+            Log_OC.e(TAG, "HTTP violation while " + doing, e);
+
+        } else if (e instanceof IOException) {
+            Log_OC.e(TAG, "Unrecovered transport exception while " + doing, e);
+
+        } else {
+            Log_OC.e(TAG, "Unexpected exception while " + doing, e);
+        }
+    }
+
+    
+    /**
+     * Sets the connection and wait-for-data timeouts to be applied by default to the methods performed by this client.
+     */
+    public void setDefaultTimeouts(int defaultDataTimeout, int defaultConnectionTimeout) {
+            getParams().setSoTimeout(defaultDataTimeout);
+            getHttpConnectionManager().getParams().setConnectionTimeout(defaultConnectionTimeout);
+    }
+
+    /**
+     * Sets the base URI for the helper methods that receive paths as parameters, instead of full URLs
+     * @param uri
+     */
+    public void setBaseUri(Uri uri) {
+        mUri = uri;
+    }
+
+    public Uri getBaseUri() {
+        return mUri;
+    }
+
     @Override\r
     public int executeMethod(HostConfiguration hostconfig, final HttpMethod method, final HttpState state) throws IOException, HttpException  {\r
         if (mCredentials instanceof BearerCredentials) {\r
@@ -343,4 +341,4 @@ public class WebdavClient extends HttpClient {
         return mCredentials;\r
     }\r
 \r
-}\r
+}
index f76ee69..46923c6 100644 (file)
@@ -2,9 +2,8 @@
  *   Copyright (C) 2012  ownCloud
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
@@ -24,8 +23,9 @@ import org.apache.jackrabbit.webdav.property.DavProperty;
 import org.apache.jackrabbit.webdav.property.DavPropertyName;
 import org.apache.jackrabbit.webdav.property.DavPropertySet;
 
+import com.owncloud.android.Log_OC;
+
 import android.net.Uri;
-import android.util.Log;
 
 public class WebdavEntry {
     private String mName, mPath, mUri, mContentType;
@@ -61,7 +61,7 @@ public class WebdavEntry {
                 }
             }
             
-            // check if it's a folder in the standard way: see RFC2518 12.2 , or RFC4918 14.3 
+            // check if it's a folder in the standard way: see RFC2518 12.2 . RFC4918 14.3 
             prop = propSet.get(DavPropertyName.RESOURCETYPE);
             if (prop!= null) {
                 Object value = prop.getValue();
@@ -89,7 +89,7 @@ public class WebdavEntry {
             }
 
         } else {
-            Log.e("WebdavEntry",
+            Log_OC.e("WebdavEntry",
                     "General fuckup, no status for webdav response");
         }
     }
index 7e2d684..132a1f9 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index 528392a..db504a8 100644 (file)
@@ -3,9 +3,8 @@
  *   Copyright (C) 2012-2013 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation, either version 2 of the License, or
- *   (at your option) any later version.
+ *   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
index add7251..db860a3 100644 (file)
@@ -299,8 +299,7 @@ the "copyright" line and a pointer to where the full notice is found.
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
+  
     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the