Update packageName
authormasensio <masensio@solidgear.es>
Tue, 29 Oct 2013 13:10:42 +0000 (14:10 +0100)
committermasensio <masensio@solidgear.es>
Tue, 29 Oct 2013 13:10:42 +0000 (14:10 +0100)
263 files changed:
AndroidManifest.xml
oc_jb_workaround/AndroidManifest.xml
oc_jb_workaround/src/com/owncloud/android/workaround/accounts/AccountAuthenticatorService.java [new file with mode: 0644]
oc_jb_workaround/src/de/mobilcom/debitel/cloud/android/workaround/accounts/AccountAuthenticatorService.java [deleted file]
pom.xml
res/layout-land/account_setup.xml
res/layout-v14/generic_explanation.xml
res/layout/account_setup.xml
res/layout/extensions_available_dialog.xml
res/layout/failed_upload_files.xml
res/layout/failed_upload_message_view.xml
res/layout/file_preview.xml
res/layout/generic_explanation.xml
res/layout/list_fragment.xml
res/layout/log_send_file.xml
res/layout/no_account_available.xml
res/layout/pincodelock.xml
res/layout/ssl_validator_layout.xml
res/layout/sso_dialog.xml
res/layout/upload_files_layout.xml
res/layout/uploader_layout.xml
res/raw-de/changelog.html
res/raw-es/changelog.html
res/raw/changelog.html
res/values/oauth2_configuration.xml
res/values/setup.xml
src/com/owncloud/android/DisplayUtils.java [new file with mode: 0644]
src/com/owncloud/android/Log_OC.java [new file with mode: 0644]
src/com/owncloud/android/MainApp.java [new file with mode: 0644]
src/com/owncloud/android/OwnCloudSession.java [new file with mode: 0644]
src/com/owncloud/android/Uploader.java [new file with mode: 0644]
src/com/owncloud/android/authentication/AccountAuthenticator.java [new file with mode: 0644]
src/com/owncloud/android/authentication/AccountAuthenticatorActivity.java [new file with mode: 0644]
src/com/owncloud/android/authentication/AccountAuthenticatorService.java [new file with mode: 0644]
src/com/owncloud/android/authentication/AccountUtils.java [new file with mode: 0644]
src/com/owncloud/android/authentication/AuthenticatorActivity.java [new file with mode: 0644]
src/com/owncloud/android/authentication/OAuth2Constants.java [new file with mode: 0644]
src/com/owncloud/android/authentication/SsoWebViewClient.java [new file with mode: 0644]
src/com/owncloud/android/datamodel/DataStorageManager.java [new file with mode: 0644]
src/com/owncloud/android/datamodel/FileDataStorageManager.java [new file with mode: 0644]
src/com/owncloud/android/datamodel/OCFile.java [new file with mode: 0644]
src/com/owncloud/android/db/DbHandler.java [new file with mode: 0644]
src/com/owncloud/android/db/ProviderMeta.java [new file with mode: 0644]
src/com/owncloud/android/extensions/ExtensionsAvailableActivity.java [new file with mode: 0644]
src/com/owncloud/android/extensions/ExtensionsAvailableDialog.java [new file with mode: 0644]
src/com/owncloud/android/extensions/ExtensionsListActivity.java [new file with mode: 0644]
src/com/owncloud/android/files/BootupBroadcastReceiver.java [new file with mode: 0644]
src/com/owncloud/android/files/FileHandler.java [new file with mode: 0644]
src/com/owncloud/android/files/InstantUploadBroadcastReceiver.java [new file with mode: 0644]
src/com/owncloud/android/files/OwnCloudFileObserver.java [new file with mode: 0644]
src/com/owncloud/android/files/managers/OCNotificationManager.java [new file with mode: 0644]
src/com/owncloud/android/files/services/FileDownloader.java [new file with mode: 0644]
src/com/owncloud/android/files/services/FileObserverService.java [new file with mode: 0644]
src/com/owncloud/android/files/services/FileUploader.java [new file with mode: 0644]
src/com/owncloud/android/files/services/OnUploadCompletedListener.java [new file with mode: 0644]
src/com/owncloud/android/location/LocationServiceLauncherReciever.java [new file with mode: 0644]
src/com/owncloud/android/location/LocationUpdateService.java [new file with mode: 0644]
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 [new file with mode: 0644]
src/com/owncloud/android/network/AdvancedX509TrustManager.java [new file with mode: 0644]
src/com/owncloud/android/network/BearerAuthScheme.java [new file with mode: 0644]
src/com/owncloud/android/network/BearerCredentials.java [new file with mode: 0644]
src/com/owncloud/android/network/CertificateCombinedException.java [new file with mode: 0644]
src/com/owncloud/android/network/OwnCloudClientUtils.java [new file with mode: 0644]
src/com/owncloud/android/network/ProgressiveDataTransferer.java [new file with mode: 0644]
src/com/owncloud/android/operations/ChunkedUploadFileOperation.java [new file with mode: 0644]
src/com/owncloud/android/operations/CreateFolderOperation.java [new file with mode: 0644]
src/com/owncloud/android/operations/DownloadFileOperation.java [new file with mode: 0644]
src/com/owncloud/android/operations/ExistenceCheckOperation.java [new file with mode: 0644]
src/com/owncloud/android/operations/OAuth2GetAccessToken.java [new file with mode: 0644]
src/com/owncloud/android/operations/OnRemoteOperationListener.java [new file with mode: 0644]
src/com/owncloud/android/operations/OperationCancelledException.java [new file with mode: 0644]
src/com/owncloud/android/operations/OwnCloudServerCheckOperation.java [new file with mode: 0644]
src/com/owncloud/android/operations/RemoteOperation.java [new file with mode: 0644]
src/com/owncloud/android/operations/RemoteOperationResult.java [new file with mode: 0644]
src/com/owncloud/android/operations/RemoveFileOperation.java [new file with mode: 0644]
src/com/owncloud/android/operations/RenameFileOperation.java [new file with mode: 0644]
src/com/owncloud/android/operations/SynchronizeFileOperation.java [new file with mode: 0644]
src/com/owncloud/android/operations/SynchronizeFolderOperation.java [new file with mode: 0644]
src/com/owncloud/android/operations/UpdateOCVersionOperation.java [new file with mode: 0644]
src/com/owncloud/android/operations/UploadFileOperation.java [new file with mode: 0644]
src/com/owncloud/android/providers/FileContentProvider.java [new file with mode: 0644]
src/com/owncloud/android/syncadapter/AbstractOwnCloudSyncAdapter.java [new file with mode: 0644]
src/com/owncloud/android/syncadapter/ContactSyncAdapter.java [new file with mode: 0644]
src/com/owncloud/android/syncadapter/ContactSyncService.java [new file with mode: 0644]
src/com/owncloud/android/syncadapter/FileSyncAdapter.java [new file with mode: 0644]
src/com/owncloud/android/syncadapter/FileSyncService.java [new file with mode: 0644]
src/com/owncloud/android/ui/ActionItem.java [new file with mode: 0644]
src/com/owncloud/android/ui/CustomButton.java [new file with mode: 0644]
src/com/owncloud/android/ui/CustomPopup.java [new file with mode: 0644]
src/com/owncloud/android/ui/ExtendedListView.java [new file with mode: 0644]
src/com/owncloud/android/ui/QuickAction.java [new file with mode: 0644]
src/com/owncloud/android/ui/activity/AccountSelectActivity.java [new file with mode: 0644]
src/com/owncloud/android/ui/activity/ConflictsResolveActivity.java [new file with mode: 0644]
src/com/owncloud/android/ui/activity/ErrorsWhileCopyingHandlerActivity.java [new file with mode: 0644]
src/com/owncloud/android/ui/activity/FailedUploadActivity.java [new file with mode: 0644]
src/com/owncloud/android/ui/activity/FileActivity.java [new file with mode: 0644]
src/com/owncloud/android/ui/activity/FileDisplayActivity.java [new file with mode: 0644]
src/com/owncloud/android/ui/activity/GenericExplanationActivity.java [new file with mode: 0644]
src/com/owncloud/android/ui/activity/InstantUploadActivity.java [new file with mode: 0644]
src/com/owncloud/android/ui/activity/LandingActivity.java [new file with mode: 0644]
src/com/owncloud/android/ui/activity/LogHistoryActivity.java [new file with mode: 0644]
src/com/owncloud/android/ui/activity/PinCodeActivity.java [new file with mode: 0644]
src/com/owncloud/android/ui/activity/Preferences.java [new file with mode: 0644]
src/com/owncloud/android/ui/activity/PreferencesNewSession.java [new file with mode: 0644]
src/com/owncloud/android/ui/activity/TransferServiceGetter.java [new file with mode: 0644]
src/com/owncloud/android/ui/activity/UploadFilesActivity.java [new file with mode: 0644]
src/com/owncloud/android/ui/adapter/FileListListAdapter.java [new file with mode: 0644]
src/com/owncloud/android/ui/adapter/LandingScreenAdapter.java [new file with mode: 0644]
src/com/owncloud/android/ui/adapter/LocalFileListAdapter.java [new file with mode: 0644]
src/com/owncloud/android/ui/adapter/LogListAdapter.java [new file with mode: 0644]
src/com/owncloud/android/ui/dialog/ChangelogDialog.java [new file with mode: 0644]
src/com/owncloud/android/ui/dialog/ConflictsResolveDialog.java [new file with mode: 0644]
src/com/owncloud/android/ui/dialog/EditNameDialog.java [new file with mode: 0644]
src/com/owncloud/android/ui/dialog/IndeterminateProgressDialog.java [new file with mode: 0644]
src/com/owncloud/android/ui/dialog/LoadingDialog.java [new file with mode: 0644]
src/com/owncloud/android/ui/dialog/SamlWebViewDialog.java [new file with mode: 0644]
src/com/owncloud/android/ui/dialog/SslValidatorDialog.java [new file with mode: 0644]
src/com/owncloud/android/ui/dialog/SsoWebView.java [new file with mode: 0644]
src/com/owncloud/android/ui/fragment/AuthenticatorAccountDetailsFragment.java [new file with mode: 0644]
src/com/owncloud/android/ui/fragment/AuthenticatorGetStartedFragment.java [new file with mode: 0644]
src/com/owncloud/android/ui/fragment/ConfirmationDialogFragment.java [new file with mode: 0644]
src/com/owncloud/android/ui/fragment/ExtendedListFragment.java [new file with mode: 0644]
src/com/owncloud/android/ui/fragment/FileDetailFragment.java [new file with mode: 0644]
src/com/owncloud/android/ui/fragment/FileFragment.java [new file with mode: 0644]
src/com/owncloud/android/ui/fragment/LandingPageFragment.java [new file with mode: 0644]
src/com/owncloud/android/ui/fragment/LocalFileListFragment.java [new file with mode: 0644]
src/com/owncloud/android/ui/fragment/OCFileListFragment.java [new file with mode: 0644]
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 [new file with mode: 0644]
src/com/owncloud/android/utils/OwnCloudVersion.java [new file with mode: 0644]
src/com/owncloud/android/utils/RecursiveFileObserver.java [new file with mode: 0644]
src/com/owncloud/android/widgets/ActionEditText.java [new file with mode: 0644]
src/de/mobilcom/debitel/cloud/android/DisplayUtils.java [deleted file]
src/de/mobilcom/debitel/cloud/android/Log_OC.java [deleted file]
src/de/mobilcom/debitel/cloud/android/MainApp.java [deleted file]
src/de/mobilcom/debitel/cloud/android/OwnCloudSession.java [deleted file]
src/de/mobilcom/debitel/cloud/android/Uploader.java [deleted file]
src/de/mobilcom/debitel/cloud/android/authentication/AccountAuthenticator.java [deleted file]
src/de/mobilcom/debitel/cloud/android/authentication/AccountAuthenticatorActivity.java [deleted file]
src/de/mobilcom/debitel/cloud/android/authentication/AccountAuthenticatorService.java [deleted file]
src/de/mobilcom/debitel/cloud/android/authentication/AccountUtils.java [deleted file]
src/de/mobilcom/debitel/cloud/android/authentication/AuthenticatorActivity.java [deleted file]
src/de/mobilcom/debitel/cloud/android/authentication/OAuth2Constants.java [deleted file]
src/de/mobilcom/debitel/cloud/android/authentication/SsoWebViewClient.java [deleted file]
src/de/mobilcom/debitel/cloud/android/datamodel/DataStorageManager.java [deleted file]
src/de/mobilcom/debitel/cloud/android/datamodel/FileDataStorageManager.java [deleted file]
src/de/mobilcom/debitel/cloud/android/datamodel/OCFile.java [deleted file]
src/de/mobilcom/debitel/cloud/android/db/DbHandler.java [deleted file]
src/de/mobilcom/debitel/cloud/android/db/ProviderMeta.java [deleted file]
src/de/mobilcom/debitel/cloud/android/extensions/ExtensionsAvailableActivity.java [deleted file]
src/de/mobilcom/debitel/cloud/android/extensions/ExtensionsAvailableDialog.java [deleted file]
src/de/mobilcom/debitel/cloud/android/extensions/ExtensionsListActivity.java [deleted file]
src/de/mobilcom/debitel/cloud/android/files/BootupBroadcastReceiver.java [deleted file]
src/de/mobilcom/debitel/cloud/android/files/FileHandler.java [deleted file]
src/de/mobilcom/debitel/cloud/android/files/InstantUploadBroadcastReceiver.java [deleted file]
src/de/mobilcom/debitel/cloud/android/files/OwnCloudFileObserver.java [deleted file]
src/de/mobilcom/debitel/cloud/android/files/managers/OCNotificationManager.java [deleted file]
src/de/mobilcom/debitel/cloud/android/files/services/FileDownloader.java [deleted file]
src/de/mobilcom/debitel/cloud/android/files/services/FileObserverService.java [deleted file]
src/de/mobilcom/debitel/cloud/android/files/services/FileUploader.java [deleted file]
src/de/mobilcom/debitel/cloud/android/files/services/OnUploadCompletedListener.java [deleted file]
src/de/mobilcom/debitel/cloud/android/location/LocationServiceLauncherReciever.java [deleted file]
src/de/mobilcom/debitel/cloud/android/location/LocationUpdateService.java [deleted file]
src/de/mobilcom/debitel/cloud/android/media/MediaControlView.java [deleted file]
src/de/mobilcom/debitel/cloud/android/media/MediaService.java [deleted file]
src/de/mobilcom/debitel/cloud/android/media/MediaServiceBinder.java [deleted file]
src/de/mobilcom/debitel/cloud/android/network/AdvancedSslSocketFactory.java [deleted file]
src/de/mobilcom/debitel/cloud/android/network/AdvancedX509TrustManager.java [deleted file]
src/de/mobilcom/debitel/cloud/android/network/BearerAuthScheme.java [deleted file]
src/de/mobilcom/debitel/cloud/android/network/BearerCredentials.java [deleted file]
src/de/mobilcom/debitel/cloud/android/network/CertificateCombinedException.java [deleted file]
src/de/mobilcom/debitel/cloud/android/network/OwnCloudClientUtils.java [deleted file]
src/de/mobilcom/debitel/cloud/android/network/ProgressiveDataTransferer.java [deleted file]
src/de/mobilcom/debitel/cloud/android/operations/ChunkedUploadFileOperation.java [deleted file]
src/de/mobilcom/debitel/cloud/android/operations/CreateFolderOperation.java [deleted file]
src/de/mobilcom/debitel/cloud/android/operations/DownloadFileOperation.java [deleted file]
src/de/mobilcom/debitel/cloud/android/operations/ExistenceCheckOperation.java [deleted file]
src/de/mobilcom/debitel/cloud/android/operations/OAuth2GetAccessToken.java [deleted file]
src/de/mobilcom/debitel/cloud/android/operations/OnRemoteOperationListener.java [deleted file]
src/de/mobilcom/debitel/cloud/android/operations/OperationCancelledException.java [deleted file]
src/de/mobilcom/debitel/cloud/android/operations/OwnCloudServerCheckOperation.java [deleted file]
src/de/mobilcom/debitel/cloud/android/operations/RemoteOperation.java [deleted file]
src/de/mobilcom/debitel/cloud/android/operations/RemoteOperationResult.java [deleted file]
src/de/mobilcom/debitel/cloud/android/operations/RemoveFileOperation.java [deleted file]
src/de/mobilcom/debitel/cloud/android/operations/RenameFileOperation.java [deleted file]
src/de/mobilcom/debitel/cloud/android/operations/SynchronizeFileOperation.java [deleted file]
src/de/mobilcom/debitel/cloud/android/operations/SynchronizeFolderOperation.java [deleted file]
src/de/mobilcom/debitel/cloud/android/operations/UpdateOCVersionOperation.java [deleted file]
src/de/mobilcom/debitel/cloud/android/operations/UploadFileOperation.java [deleted file]
src/de/mobilcom/debitel/cloud/android/providers/FileContentProvider.java [deleted file]
src/de/mobilcom/debitel/cloud/android/syncadapter/AbstractOwnCloudSyncAdapter.java [deleted file]
src/de/mobilcom/debitel/cloud/android/syncadapter/ContactSyncAdapter.java [deleted file]
src/de/mobilcom/debitel/cloud/android/syncadapter/ContactSyncService.java [deleted file]
src/de/mobilcom/debitel/cloud/android/syncadapter/FileSyncAdapter.java [deleted file]
src/de/mobilcom/debitel/cloud/android/syncadapter/FileSyncService.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/ActionItem.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/CustomButton.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/CustomPopup.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/ExtendedListView.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/QuickAction.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/activity/AccountSelectActivity.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/activity/ConflictsResolveActivity.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/activity/ErrorsWhileCopyingHandlerActivity.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/activity/FailedUploadActivity.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/activity/FileActivity.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/activity/FileDisplayActivity.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/activity/GenericExplanationActivity.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/activity/InstantUploadActivity.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/activity/LandingActivity.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/activity/LogHistoryActivity.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/activity/PinCodeActivity.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/activity/Preferences.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/activity/PreferencesNewSession.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/activity/TransferServiceGetter.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/activity/UploadFilesActivity.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/adapter/FileListListAdapter.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/adapter/LandingScreenAdapter.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/adapter/LocalFileListAdapter.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/adapter/LogListAdapter.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/dialog/ChangelogDialog.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/dialog/ConflictsResolveDialog.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/dialog/EditNameDialog.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/dialog/IndeterminateProgressDialog.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/dialog/LoadingDialog.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/dialog/SamlWebViewDialog.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/dialog/SslValidatorDialog.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/dialog/SsoWebView.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/fragment/AuthenticatorAccountDetailsFragment.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/fragment/AuthenticatorGetStartedFragment.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/fragment/ConfirmationDialogFragment.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/fragment/ExtendedListFragment.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/fragment/FileDetailFragment.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/fragment/FileFragment.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/fragment/LandingPageFragment.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/fragment/LocalFileListFragment.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/fragment/OCFileListFragment.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/preview/FileDownloadFragment.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/preview/PreviewImageActivity.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/preview/PreviewImageFragment.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/preview/PreviewImagePagerAdapter.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/preview/PreviewMediaFragment.java [deleted file]
src/de/mobilcom/debitel/cloud/android/ui/preview/PreviewVideoActivity.java [deleted file]
src/de/mobilcom/debitel/cloud/android/utils/FileStorageUtils.java [deleted file]
src/de/mobilcom/debitel/cloud/android/utils/OwnCloudVersion.java [deleted file]
src/de/mobilcom/debitel/cloud/android/utils/RecursiveFileObserver.java [deleted file]
src/de/mobilcom/debitel/cloud/android/widgets/ActionEditText.java [deleted file]
src/eu/alefzero/webdav/ChunkFromFileChannelRequestEntity.java
src/eu/alefzero/webdav/FileRequestEntity.java
src/eu/alefzero/webdav/WebdavClient.java
src/eu/alefzero/webdav/WebdavEntry.java
tests/AndroidManifest.xml
tests/src/com/owncloud/android/test/AccountUtilsTest.java [new file with mode: 0644]
tests/src/com/owncloud/android/test/FileContentProviderTest.java [new file with mode: 0644]
tests/src/de/mobilcom/debitel/cloud/android/test/AccountUtilsTest.java [deleted file]
tests/src/de/mobilcom/debitel/cloud/android/test/FileContentProviderTest.java [deleted file]

index 0b2e910..f448d53 100644 (file)
@@ -17,7 +17,7 @@
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
  -->
   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="de.mobilcom.debitel.cloud.android"
+<manifest package="com.owncloud.android"
     android:versionCode="104006"
     android:versionName="1.4.6" xmlns:android="http://schemas.android.com/apk/res/android">
 
     android:versionCode="104006"
     android:versionName="1.4.6" xmlns:android="http://schemas.android.com/apk/res/android">
 
         <service android:name=".media.MediaService" />
         
         <activity android:name=".ui.activity.PinCodeActivity" />
         <service android:name=".media.MediaService" />
         
         <activity android:name=".ui.activity.PinCodeActivity" />
-        <activity android:name=".extensions.ExtensionsAvailableActivity"></activity>
-        <activity android:name=".extensions.ExtensionsListActivity"></activity>
+        <activity android:name="com.owncloud.android.extensions.ExtensionsAvailableActivity"></activity>
+        <activity android:name="com.owncloud.android.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"/>
         <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"/>
index 31ec731..c987c82 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="de.mobilcom.debitel.cloud.android.workaround.accounts"
+    package="com.owncloud.android.workaround.accounts"
     android:versionCode="0100008"
     android:versionName="1.0.8" >
 
     android:versionCode="0100008"
     android:versionName="1.0.8" >
 
diff --git a/oc_jb_workaround/src/com/owncloud/android/workaround/accounts/AccountAuthenticatorService.java b/oc_jb_workaround/src/com/owncloud/android/workaround/accounts/AccountAuthenticatorService.java
new file mode 100644 (file)
index 0000000..5a7c57e
--- /dev/null
@@ -0,0 +1,138 @@
+/* ownCloud Jelly Bean Workaround for lost credentials
+ *   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 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.workaround.accounts;
+
+import android.accounts.AbstractAccountAuthenticator;
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.AccountManager;
+import android.accounts.NetworkErrorException;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+//import android.util.Log;
+
+public class AccountAuthenticatorService extends Service {
+
+    private AccountAuthenticator mAuthenticator;
+    //static final public String ACCOUNT_TYPE = "owncloud";
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mAuthenticator = new AccountAuthenticator(this);
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mAuthenticator.getIBinder();
+    }
+    
+    
+    public static class AccountAuthenticator extends AbstractAccountAuthenticator {
+
+        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 AccountAuthenticator(Context context) {
+            super(context);
+        }
+
+        @Override
+        public Bundle addAccount(AccountAuthenticatorResponse response,
+                String accountType, String authTokenType,
+                String[] requiredFeatures, Bundle options)
+                throws NetworkErrorException {
+               //Log.e("WORKAROUND", "Yes, WORKAROUND takes the control here");
+            final Intent intent = new Intent("com.owncloud.android.workaround.accounts.CREATE");
+            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.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
+            intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+            intent.addFlags(Intent.FLAG_FROM_BACKGROUND);
+
+            final Bundle bundle = new Bundle();
+            bundle.putParcelable(AccountManager.KEY_INTENT, intent);
+            return bundle;
+            //return getCommonResultBundle();
+        }
+
+        
+               @Override
+        public Bundle confirmCredentials(AccountAuthenticatorResponse response,
+                Account account, Bundle options) throws NetworkErrorException {
+            return getCommonResultBundle();
+        }
+
+        @Override
+        public Bundle editProperties(AccountAuthenticatorResponse response,
+                String accountType) {
+            return getCommonResultBundle();
+        }
+
+        @Override
+        public Bundle getAuthToken(AccountAuthenticatorResponse response,
+                Account account, String authTokenType, Bundle options)
+                throws NetworkErrorException {
+            return getCommonResultBundle();
+        }
+
+        @Override
+        public String getAuthTokenLabel(String authTokenType) {
+            return "";
+        }
+
+        @Override
+        public Bundle hasFeatures(AccountAuthenticatorResponse response,
+                Account account, String[] features) throws NetworkErrorException {
+            return getCommonResultBundle();
+        }
+
+        @Override
+        public Bundle updateCredentials(AccountAuthenticatorResponse response,
+                Account account, String authTokenType, Bundle options)
+                throws NetworkErrorException {
+            return getCommonResultBundle();
+        }
+
+        @Override
+        public Bundle getAccountRemovalAllowed(
+                AccountAuthenticatorResponse response, Account account)
+                throws NetworkErrorException {
+            return super.getAccountRemovalAllowed(response, account);
+        }
+
+        private Bundle getCommonResultBundle() {
+               Bundle resultBundle = new Bundle();
+            resultBundle.putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION);
+            resultBundle.putString(AccountManager.KEY_ERROR_MESSAGE, "This is just a workaround, not a real account authenticator");
+            return resultBundle;
+               }
+
+    }
+}
diff --git a/oc_jb_workaround/src/de/mobilcom/debitel/cloud/android/workaround/accounts/AccountAuthenticatorService.java b/oc_jb_workaround/src/de/mobilcom/debitel/cloud/android/workaround/accounts/AccountAuthenticatorService.java
deleted file mode 100644 (file)
index e8e4933..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-/* ownCloud Jelly Bean Workaround for lost credentials
- *   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 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 de.mobilcom.debitel.cloud.android.workaround.accounts;
-
-import android.accounts.AbstractAccountAuthenticator;
-import android.accounts.Account;
-import android.accounts.AccountAuthenticatorResponse;
-import android.accounts.AccountManager;
-import android.accounts.NetworkErrorException;
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.IBinder;
-//import android.util.Log;
-
-public class AccountAuthenticatorService extends Service {
-
-    private AccountAuthenticator mAuthenticator;
-    //static final public String ACCOUNT_TYPE = "owncloud";
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mAuthenticator = new AccountAuthenticator(this);
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return mAuthenticator.getIBinder();
-    }
-    
-    
-    public static class AccountAuthenticator extends AbstractAccountAuthenticator {
-
-        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 AccountAuthenticator(Context context) {
-            super(context);
-        }
-
-        @Override
-        public Bundle addAccount(AccountAuthenticatorResponse response,
-                String accountType, String authTokenType,
-                String[] requiredFeatures, Bundle options)
-                throws NetworkErrorException {
-               //Log.e("WORKAROUND", "Yes, WORKAROUND takes the control here");
-            final Intent intent = new Intent("de.mobilcom.debitel.cloud.android.workaround.accounts.CREATE");
-            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.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
-            intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
-            intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-            intent.addFlags(Intent.FLAG_FROM_BACKGROUND);
-
-            final Bundle bundle = new Bundle();
-            bundle.putParcelable(AccountManager.KEY_INTENT, intent);
-            return bundle;
-            //return getCommonResultBundle();
-        }
-
-        
-               @Override
-        public Bundle confirmCredentials(AccountAuthenticatorResponse response,
-                Account account, Bundle options) throws NetworkErrorException {
-            return getCommonResultBundle();
-        }
-
-        @Override
-        public Bundle editProperties(AccountAuthenticatorResponse response,
-                String accountType) {
-            return getCommonResultBundle();
-        }
-
-        @Override
-        public Bundle getAuthToken(AccountAuthenticatorResponse response,
-                Account account, String authTokenType, Bundle options)
-                throws NetworkErrorException {
-            return getCommonResultBundle();
-        }
-
-        @Override
-        public String getAuthTokenLabel(String authTokenType) {
-            return "";
-        }
-
-        @Override
-        public Bundle hasFeatures(AccountAuthenticatorResponse response,
-                Account account, String[] features) throws NetworkErrorException {
-            return getCommonResultBundle();
-        }
-
-        @Override
-        public Bundle updateCredentials(AccountAuthenticatorResponse response,
-                Account account, String authTokenType, Bundle options)
-                throws NetworkErrorException {
-            return getCommonResultBundle();
-        }
-
-        @Override
-        public Bundle getAccountRemovalAllowed(
-                AccountAuthenticatorResponse response, Account account)
-                throws NetworkErrorException {
-            return super.getAccountRemovalAllowed(response, account);
-        }
-
-        private Bundle getCommonResultBundle() {
-               Bundle resultBundle = new Bundle();
-            resultBundle.putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION);
-            resultBundle.putString(AccountManager.KEY_ERROR_MESSAGE, "This is just a workaround, not a real account authenticator");
-            return resultBundle;
-               }
-
-    }
-}
diff --git a/pom.xml b/pom.xml
index e13b7ab..a55a77a 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 
     <modelVersion>4.0.0</modelVersion>
          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>de.mobilcom.debitel.cloud.android</groupId>
+    <groupId>com.owncloud.android</groupId>
     <artifactId>owncloud</artifactId>
     <version>1.3.21-SNAPSHOT</version>
     <packaging>apk</packaging>
     <artifactId>owncloud</artifactId>
     <version>1.3.21-SNAPSHOT</version>
     <packaging>apk</packaging>
index b0a42ae..3500c0f 100644 (file)
                                                           \r
        </LinearLayout>\r
        \r
                                                           \r
        </LinearLayout>\r
        \r
-       <de.mobilcom.debitel.cloud.android.ui.CustomButton\r
+       <com.owncloud.android.ui.CustomButton\r
                android:id="@id/buttonOK"\r
                android:layout_width="match_parent"\r
                android:layout_height="wrap_content"\r
                android:id="@id/buttonOK"\r
                android:layout_width="match_parent"\r
                android:layout_height="wrap_content"\r
index e3d50ca..dee1507 100644 (file)
         android:orientation="horizontal" >
 
         <!-- 'OK' / 'CANCEL' BUTTONS CHANGE THEIR ORDER FROM ANDROID 4.0 ; THANKS, GOOGLE -->
         android:orientation="horizontal" >
 
         <!-- 'OK' / 'CANCEL' BUTTONS CHANGE THEIR ORDER FROM ANDROID 4.0 ; THANKS, GOOGLE -->
-        <de.mobilcom.debitel.cloud.android.ui.CustomButton
+        <com.owncloud.android.ui.CustomButton
             android:id="@+id/cancel"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:text="@string/common_cancel" />
 
             android:id="@+id/cancel"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:text="@string/common_cancel" />
 
-               <de.mobilcom.debitel.cloud.android.ui.CustomButton
+               <com.owncloud.android.ui.CustomButton
                    android:id="@+id/ok"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:id="@+id/ok"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
index b1f621a..a0d5470 100644 (file)
             android:gravity="center_vertical"\r
             android:text="@string/auth_unauthorized" />\r
 \r
             android:gravity="center_vertical"\r
             android:text="@string/auth_unauthorized" />\r
 \r
-        <de.mobilcom.debitel.cloud.android.ui.CustomButton\r
+        <com.owncloud.android.ui.CustomButton\r
             android:id="@+id/buttonOK"\r
             android:layout_width="match_parent"\r
             android:layout_height="wrap_content"\r
             android:id="@+id/buttonOK"\r
             android:layout_width="match_parent"\r
             android:layout_height="wrap_content"\r
index 64d1021..61b920e 100644 (file)
         android:layout_margin="5dp"
         android:weightSum="1.0" >
 
         android:layout_margin="5dp"
         android:weightSum="1.0" >
 
-        <de.mobilcom.debitel.cloud.android.ui.CustomButton
+        <com.owncloud.android.ui.CustomButton
             android:id="@+id/buttonNo"
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:text="@string/common_no"
             android:layout_weight="0.5"/>
 
             android:id="@+id/buttonNo"
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:text="@string/common_no"
             android:layout_weight="0.5"/>
 
-        <de.mobilcom.debitel.cloud.android.ui.CustomButton
+        <com.owncloud.android.ui.CustomButton
             android:id="@+id/buttonYes"
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:id="@+id/buttonYes"
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
index 305c5e6..9335045 100644 (file)
@@ -63,7 +63,7 @@
                 android:text="@string/failed_upload_all_cb"\r
                 android:textSize="8sp" />\r
 \r
                 android:text="@string/failed_upload_all_cb"\r
                 android:textSize="8sp" />\r
 \r
-            <de.mobilcom.debitel.cloud.android.ui.CustomButton\r
+            <com.owncloud.android.ui.CustomButton\r
                 android:id="@+id/failed_upload_retry_all_btn"\r
                 android:layout_width="wrap_content"\r
                 android:layout_height="wrap_content"\r
                 android:id="@+id/failed_upload_retry_all_btn"\r
                 android:layout_width="wrap_content"\r
                 android:layout_height="wrap_content"\r
@@ -72,7 +72,7 @@
                 android:text="@string/failed_upload_headline_retryall_btn"\r
                 android:textSize="8sp" />\r
 \r
                 android:text="@string/failed_upload_headline_retryall_btn"\r
                 android:textSize="8sp" />\r
 \r
-            <de.mobilcom.debitel.cloud.android.ui.CustomButton\r
+            <com.owncloud.android.ui.CustomButton\r
                 android:id="@+id/failed_upload_delete_all_btn"\r
                 android:layout_width="wrap_content"\r
                 android:layout_height="wrap_content"\r
                 android:id="@+id/failed_upload_delete_all_btn"\r
                 android:layout_width="wrap_content"\r
                 android:layout_height="wrap_content"\r
index da5808c..872f28a 100644 (file)
@@ -10,7 +10,7 @@
        android:layout_height="wrap_content"\r
        android:minWidth="100dp"/>\r
    \r
        android:layout_height="wrap_content"\r
        android:minWidth="100dp"/>\r
    \r
-   <de.mobilcom.debitel.cloud.android.ui.CustomButton\r
+   <com.owncloud.android.ui.CustomButton\r
     android:id="@+id/failed_uploadactivity_close_button"\r
     android:layout_width="fill_parent"\r
     android:layout_height="wrap_content"\r
     android:id="@+id/failed_uploadactivity_close_button"\r
     android:layout_width="fill_parent"\r
     android:layout_height="wrap_content"\r
index 12f609f..483a369 100644 (file)
@@ -54,7 +54,7 @@
                
        </FrameLayout>
        
                
        </FrameLayout>
        
-       <de.mobilcom.debitel.cloud.android.media.MediaControlView 
+       <com.owncloud.android.media.MediaControlView 
            android:id="@id/media_controller"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@id/media_controller"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
index 4249bc6..fd2d05b 100644 (file)
         android:gravity="center"
         android:orientation="horizontal" >
 
         android:gravity="center"
         android:orientation="horizontal" >
 
-               <de.mobilcom.debitel.cloud.android.ui.CustomButton
+               <com.owncloud.android.ui.CustomButton
                    android:id="@+id/ok"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:text="@string/common_ok" />
                
                    android:id="@+id/ok"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:text="@string/common_ok" />
                
-        <de.mobilcom.debitel.cloud.android.ui.CustomButton
+        <com.owncloud.android.ui.CustomButton
             android:id="@+id/cancel"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:id="@+id/cancel"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
index b34c719..00e6b41 100644 (file)
@@ -23,7 +23,7 @@
        android:layout_weight="1"
        android:orientation="vertical" >
 
        android:layout_weight="1"
        android:orientation="vertical" >
 
-       <de.mobilcom.debitel.cloud.android.ui.ExtendedListView
+       <com.owncloud.android.ui.ExtendedListView
         android:id="@+id/list_root"
         android:layout_width="match_parent"
         android:layout_height="0dip"
         android:id="@+id/list_root"
         android:layout_width="match_parent"
         android:layout_height="0dip"
index d836844..05f5c57 100644 (file)
@@ -24,7 +24,7 @@
          android:layout_height="wrap_content"
          android:gravity="bottom">
         
          android:layout_height="wrap_content"
          android:gravity="bottom">
         
-     <de.mobilcom.debitel.cloud.android.ui.CustomButton
+     <com.owncloud.android.ui.CustomButton
                android:id="@+id/deleteLogHistoryButton"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                android:id="@+id/deleteLogHistoryButton"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
index 3d933c1..11c8739 100644 (file)
@@ -39,7 +39,7 @@
             android:text="@string/main_wrn_accsetup"
             android:textAppearance="?android:attr/textAppearanceMedium" />
 
             android:text="@string/main_wrn_accsetup"
             android:textAppearance="?android:attr/textAppearanceMedium" />
 
-        <de.mobilcom.debitel.cloud.android.ui.CustomButton
+        <com.owncloud.android.ui.CustomButton
             android:id="@+id/setup_account"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:id="@+id/setup_account"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
index ca39d8d..bb13c5f 100644 (file)
@@ -68,7 +68,7 @@
             style="@style/PassCodeStyle" />\r
     </LinearLayout>\r
 \r
             style="@style/PassCodeStyle" />\r
     </LinearLayout>\r
 \r
-    <de.mobilcom.debitel.cloud.android.ui.CustomButton android:layout_width="wrap_content"\r
+    <com.owncloud.android.ui.CustomButton android:layout_width="wrap_content"\r
         android:layout_height="wrap_content"\r
         android:text="@string/common_cancel"\r
         android:textColor="@android:color/black"\r
         android:layout_height="wrap_content"\r
         android:text="@string/common_cancel"\r
         android:textColor="@android:color/black"\r
index 5106045..df3e48a 100644 (file)
         android:layout_height="wrap_content"
         android:gravity="center" >
 
         android:layout_height="wrap_content"
         android:gravity="center" >
 
-        <de.mobilcom.debitel.cloud.android.ui.CustomButton
+        <com.owncloud.android.ui.CustomButton
             android:id="@+id/cancel"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:text="@string/common_cancel" />
 
             android:id="@+id/cancel"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:text="@string/common_cancel" />
 
-        <de.mobilcom.debitel.cloud.android.ui.CustomButton
+        <com.owncloud.android.ui.CustomButton
             android:id="@+id/details_btn"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:text="@string/ssl_validator_btn_details_see" />
 
             android:id="@+id/details_btn"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:text="@string/ssl_validator_btn_details_see" />
 
-        <de.mobilcom.debitel.cloud.android.ui.CustomButton
+        <com.owncloud.android.ui.CustomButton
             android:id="@+id/ok"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:id="@+id/ok"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
index 7923e44..788a0e2 100644 (file)
@@ -21,7 +21,7 @@
     android:layout_height="wrap_content"
     >
     
     android:layout_height="wrap_content"
     >
     
-    <de.mobilcom.debitel.cloud.android.ui.dialog.SsoWebView
+    <com.owncloud.android.ui.dialog.SsoWebView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
         android:id="@+id/sso_webview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
         android:id="@+id/sso_webview"
index bfc39a6..cb0206c 100644 (file)
         android:layout_width="match_parent"\r
         android:layout_height="0dip"\r
         android:layout_weight="1"\r
         android:layout_width="match_parent"\r
         android:layout_height="0dip"\r
         android:layout_weight="1"\r
-        class="de.mobilcom.debitel.cloud.android.ui.fragment.LocalFileListFragment" />\r
+        class="com.owncloud.android.ui.fragment.LocalFileListFragment" />\r
 
     <LinearLayout\r
         android:layout_width="match_parent"\r
         android:layout_height="wrap_content"\r
         android:gravity="center"\r
         android:orientation="horizontal" >\r
 
     <LinearLayout\r
         android:layout_width="match_parent"\r
         android:layout_height="wrap_content"\r
         android:gravity="center"\r
         android:orientation="horizontal" >\r
-\r        <de.mobilcom.debitel.cloud.android.ui.CustomButton\r
+\r        <com.owncloud.android.ui.CustomButton\r
             android:id="@+id/upload_files_btn_cancel"\r
             android:layout_width="wrap_content"\r
             android:layout_height="wrap_content"\r
             android:layout_weight="1"\r
             android:text="@string/common_cancel" />\r
             android:id="@+id/upload_files_btn_cancel"\r
             android:layout_width="wrap_content"\r
             android:layout_height="wrap_content"\r
             android:layout_weight="1"\r
             android:text="@string/common_cancel" />\r
-\r              <de.mobilcom.debitel.cloud.android.ui.CustomButton\r
+\r              <com.owncloud.android.ui.CustomButton\r
                    android:id="@+id/upload_files_btn_upload"\r
                    android:layout_width="wrap_content"\r
                    android:layout_height="wrap_content"\r
                    android:id="@+id/upload_files_btn_upload"\r
                    android:layout_width="wrap_content"\r
                    android:layout_height="wrap_content"\r
index e38f4a4..2b549a7 100644 (file)
@@ -33,7 +33,7 @@
        </FrameLayout>
        <LinearLayout android:id="@+id/linearLayout1"
                android:layout_width="fill_parent" android:layout_alignParentBottom="true" android:layout_height="wrap_content" android:orientation="vertical">
        </FrameLayout>
        <LinearLayout android:id="@+id/linearLayout1"
                android:layout_width="fill_parent" android:layout_alignParentBottom="true" android:layout_height="wrap_content" android:orientation="vertical">
-               <de.mobilcom.debitel.cloud.android.ui.CustomButton android:layout_gravity="bottom" android:layout_height="wrap_content"
+               <com.owncloud.android.ui.CustomButton android:layout_gravity="bottom" android:layout_height="wrap_content"
                        android:layout_width="fill_parent" android:id="@+id/uploader_choose_folder"
                        android:text="@string/uploader_btn_upload_text"/>
        </LinearLayout>
                        android:layout_width="fill_parent" android:id="@+id/uploader_choose_folder"
                        android:text="@string/uploader_btn_upload_text"/>
        </LinearLayout>
index 7f6b746..a54a664 100644 (file)
@@ -24,7 +24,7 @@
                In dieser Version von Android existiert ein Bug, der nach jedem Neustart eine erneute Eingabe der ownCloud Login-Informationen nötig macht. Um das zu umgehen installieren Sie bitte diese kostenlose Hilfs-App: 
        </p>
        <p style="text-align:center">
                In dieser Version von Android existiert ein Bug, der nach jedem Neustart eine erneute Eingabe der ownCloud Login-Informationen nötig macht. Um das zu umgehen installieren Sie bitte diese kostenlose Hilfs-App: 
        </p>
        <p style="text-align:center">
-               <a href="http://play.google.com/store/apps/details?id=de.mobilcom.debitel.cloud.android.workaround.accounts">ownCloud Jelly Bean Workaround</a> 
+               <a href="http://play.google.com/store/apps/details?id=com.owncloud.android.workaround.accounts">ownCloud Jelly Bean Workaround</a> 
        </p>
        </body>
 </html>
        </p>
        </body>
 </html>
index 0802d39..9321d52 100644 (file)
@@ -24,7 +24,7 @@
                Para prevenir la pérdida de las credenciales de sus cuentas ownCloud en cada reinicio, por favor, instale esta app gratuita que evita el problema en Jelly Bean:        
        </p>
        <p style="text-align:center">
                Para prevenir la pérdida de las credenciales de sus cuentas ownCloud en cada reinicio, por favor, instale esta app gratuita que evita el problema en Jelly Bean:        
        </p>
        <p style="text-align:center">
-               <a href="http://play.google.com/store/apps/details?id=de.mobilcom.debitel.cloud.android.workaround.accounts">ownCloud Jelly Bean Workaround</a>
+               <a href="http://play.google.com/store/apps/details?id=com.owncloud.android.workaround.accounts">ownCloud Jelly Bean Workaround</a>
        </p>
        </body>
 </html>
        </p>
        </body>
 </html>
index f388f0f..754cf6f 100644 (file)
@@ -24,7 +24,7 @@
                To prevent losing your ownCloud account credentials on every reboot, please, install this free helper app to work around the bug in Jelly Bean:
        </p>
        <p style="text-align:center">
                To prevent losing your ownCloud account credentials on every reboot, please, install this free helper app to work around the bug in Jelly Bean:
        </p>
        <p style="text-align:center">
-               <a href="http://play.google.com/store/apps/details?id=de.mobilcom.debitel.cloud.android.workaround.accounts">ownCloud Jelly Bean Workaround</a> 
+               <a href="http://play.google.com/store/apps/details?id=com.owncloud.android.workaround.accounts">ownCloud Jelly Bean Workaround</a> 
        </p>
        </body>
 </html>
        </p>
        </body>
 </html>
index c81f54a..f8e0f51 100644 (file)
@@ -12,7 +12,7 @@
     <string name="oauth2_response_type">code</string>                          <!-- depends on oauth2_grant_type -->
     
     <!-- values that should be pre-agreed between app and authorization server, but can be loaded without rebuilding the app -->
     <string name="oauth2_response_type">code</string>                          <!-- depends on oauth2_grant_type -->
     
     <!-- values that should be pre-agreed between app and authorization server, but can be loaded without rebuilding the app -->
-    <string name="oauth2_client_id">de.mobilcom.debitel.cloud.android</string> <!-- preferable that client decides this -->
+    <string name="oauth2_client_id">com.owncloud.android</string>      <!-- preferable that client decides this -->
     <string name="oauth2_client_secret"></string>                                      <!-- preferable that client decides this -->
     
 </resources>
     <string name="oauth2_client_secret"></string>                                      <!-- preferable that client decides this -->
     
 </resources>
index 4b49701..e2460d9 100644 (file)
@@ -39,7 +39,7 @@
     <string name="url_imprint">"https://webapi.md.de/about/imprint"</string>
     <string name="mail_recommend">"mailto:"</string>
     <string name="mail_feedback">"mailto:appservice@cloud.md.de"</string>
     <string name="url_imprint">"https://webapi.md.de/about/imprint"</string>
     <string name="mail_recommend">"mailto:"</string>
     <string name="mail_feedback">"mailto:appservice@cloud.md.de"</string>
-    <!-- string name="url_app_download">"https://play.google.com/store/apps/details?id=de.mobilcom.debitel.cloud.android"</string -->
+    <!-- string name="url_app_download">"https://play.google.com/store/apps/details?id=com.owncloud.android"</string -->
 
 </resources>
 
 
 </resources>
 
diff --git a/src/com/owncloud/android/DisplayUtils.java b/src/com/owncloud/android/DisplayUtils.java
new file mode 100644 (file)
index 0000000..1ee898b
--- /dev/null
@@ -0,0 +1,190 @@
+/* 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 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;\r
+\r
+import java.util.Arrays;\r
+import java.util.Date;\r
+import java.util.HashMap;\r
+import java.util.HashSet;\r
+import java.util.Set;\r
+\r
+/**\r
+ * A helper class for some string operations.\r
+ * \r
+ * @author Bartek Przybylski\r
+ * @author David A. Velasco\r
+ */\r
+public class DisplayUtils {\r
+    \r
+    //private static String TAG = DisplayUtils.class.getSimpleName(); \r
+    \r
+    private static final String[] sizeSuffixes = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };\r
+\r
+    private static HashMap<String, String> mimeType2HUmanReadable;\r
+    static {\r
+        mimeType2HUmanReadable = new HashMap<String, String>();\r
+        // images\r
+        mimeType2HUmanReadable.put("image/jpeg", "JPEG image");\r
+        mimeType2HUmanReadable.put("image/jpg", "JPEG image");\r
+        mimeType2HUmanReadable.put("image/png", "PNG image");\r
+        mimeType2HUmanReadable.put("image/bmp", "Bitmap image");\r
+        mimeType2HUmanReadable.put("image/gif", "GIF image");\r
+        mimeType2HUmanReadable.put("image/svg+xml", "JPEG image");\r
+        mimeType2HUmanReadable.put("image/tiff", "TIFF image");\r
+        // music\r
+        mimeType2HUmanReadable.put("audio/mpeg", "MP3 music file");\r
+        mimeType2HUmanReadable.put("application/ogg", "OGG music file");\r
+\r
+    }\r
+\r
+    private static final String TYPE_APPLICATION = "application";\r
+    private static final String TYPE_AUDIO = "audio";\r
+    private static final String TYPE_IMAGE = "image";\r
+    private static final String TYPE_TXT = "text";\r
+    private static final String TYPE_VIDEO = "video";\r
+    \r
+    private static final String SUBTYPE_PDF = "pdf";\r
+    private static final String[] SUBTYPES_DOCUMENT = { "msword", "mspowerpoint", "msexcel", \r
+                                                        "vnd.oasis.opendocument.presentation",\r
+                                                        "vnd.oasis.opendocument.spreadsheet",\r
+                                                        "vnd.oasis.opendocument.text"\r
+                                                        };\r
+    private static Set<String> SUBTYPES_DOCUMENT_SET = new HashSet<String>(Arrays.asList(SUBTYPES_DOCUMENT));\r
+    private static final String[] SUBTYPES_COMPRESSED = {"x-tar", "x-gzip", "zip"};\r
+    private static final Set<String> SUBTYPES_COMPRESSED_SET = new HashSet<String>(Arrays.asList(SUBTYPES_COMPRESSED));\r
+    \r
+    /**\r
+     * Converts the file size in bytes to human readable output.\r
+     * \r
+     * @param bytes Input file size\r
+     * @return Like something readable like "12 MB"\r
+     */\r
+    public static String bytesToHumanReadable(long bytes) {\r
+        double result = bytes;\r
+        int attachedsuff = 0;\r
+        while (result > 1024 && attachedsuff < sizeSuffixes.length) {\r
+            result /= 1024.;\r
+            attachedsuff++;\r
+        }\r
+        result = ((int) (result * 100)) / 100.;\r
+        return result + " " + sizeSuffixes[attachedsuff];\r
+    }\r
+\r
+    /**\r
+     * Removes special HTML entities from a string\r
+     * \r
+     * @param s Input string\r
+     * @return A cleaned version of the string\r
+     */\r
+    public static String HtmlDecode(String s) {\r
+        /*\r
+         * TODO: Perhaps we should use something more proven like:\r
+         * http://commons.apache.org/lang/api-2.6/org/apache/commons/lang/StringEscapeUtils.html#unescapeHtml%28java.lang.String%29\r
+         */\r
+\r
+        String ret = "";\r
+        for (int i = 0; i < s.length(); ++i) {\r
+            if (s.charAt(i) == '%') {\r
+                ret += (char) Integer.parseInt(s.substring(i + 1, i + 3), 16);\r
+                i += 2;\r
+            } else {\r
+                ret += s.charAt(i);\r
+            }\r
+        }\r
+        return ret;\r
+    }\r
+\r
+    /**\r
+     * Converts MIME types like "image/jpg" to more end user friendly output\r
+     * like "JPG image".\r
+     * \r
+     * @param mimetype MIME type to convert\r
+     * @return A human friendly version of the MIME type\r
+     */\r
+    public static String convertMIMEtoPrettyPrint(String mimetype) {\r
+        if (mimeType2HUmanReadable.containsKey(mimetype)) {\r
+            return mimeType2HUmanReadable.get(mimetype);\r
+        }\r
+        if (mimetype.split("/").length >= 2)\r
+            return mimetype.split("/")[1].toUpperCase() + " file";\r
+        return "Unknown type";\r
+    }\r
+    \r
+    \r
+    /**\r
+     * Returns the resource identifier of an image resource to use as icon associated to a \r
+     * known MIME type.\r
+     * \r
+     * @param mimetype      MIME type string.\r
+     * @return              Resource identifier of an image resource.\r
+     */\r
+    public static int getResourceId(String mimetype) {\r
+\r
+        if (mimetype == null || "DIR".equals(mimetype)) {\r
+            return R.drawable.ic_menu_archive;\r
+            \r
+        } else {\r
+            String [] parts = mimetype.split("/");\r
+            String type = parts[0];\r
+            String subtype = (parts.length > 1) ? parts[1] : "";\r
+            \r
+            if(TYPE_TXT.equals(type)) {\r
+                return R.drawable.file_doc;\r
+    \r
+            } else if(TYPE_IMAGE.equals(type)) {\r
+                return R.drawable.file_image;\r
+                \r
+            } else if(TYPE_VIDEO.equals(type)) {\r
+                return R.drawable.file_movie;\r
+                \r
+            } else if(TYPE_AUDIO.equals(type)) {  \r
+                return R.drawable.file_sound;\r
+                \r
+            } else if(TYPE_APPLICATION.equals(type)) {\r
+                \r
+                if (SUBTYPE_PDF.equals(subtype)) {\r
+                    return R.drawable.file_pdf;\r
+                    \r
+                } else if (SUBTYPES_DOCUMENT_SET.contains(subtype)) {\r
+                    return R.drawable.file_doc;\r
+\r
+                } else if (SUBTYPES_COMPRESSED_SET.contains(subtype)) {\r
+                    return R.drawable.file_zip;\r
+                }\r
+    \r
+            }\r
+            // problems: RAR, RTF, 3GP are send as application/octet-stream from the server ; extension in the filename should be explicitly reviewed\r
+        }\r
+\r
+        // default icon\r
+        return R.drawable.file;\r
+    }\r
+\r
+    \r
+\r
+    /**\r
+     * Converts Unix time to human readable format\r
+     * @param miliseconds that have passed since 01/01/1970\r
+     * @return The human readable time for the users locale\r
+     */\r
+    public static String unixTimeToHumanReadable(long milliseconds) {\r
+        Date date = new Date(milliseconds);\r
+        return date.toLocaleString();\r
+    }\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..5a1be29
--- /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 + MainApp.getLogName() + 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(); 
+        } 
+    }
+}
+
+    
+   
+
+  
+
+   
+   
+}
diff --git a/src/com/owncloud/android/MainApp.java b/src/com/owncloud/android/MainApp.java
new file mode 100644 (file)
index 0000000..6cd88fe
--- /dev/null
@@ -0,0 +1,105 @@
+/* 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;
+
+import android.app.Application;
+import android.content.Context;
+/**
+ * Main Application of the project
+ * 
+ * Contains methods to build the "static" strings. These strings were before constants in different classes
+ * 
+ * @author masensio
+ */
+public class MainApp extends Application {
+
+    private static Context mContext;
+
+    public void onCreate(){
+        super.onCreate();
+        MainApp.mContext = getApplicationContext();
+    }
+
+    public static Context getAppContext() {
+        return MainApp.mContext;
+    }
+
+    // Methods to obtain Strings referring app_name 
+    //   From AccountAuthenticator 
+    //   public static final String ACCOUNT_TYPE = "owncloud";    
+    public static String getAccountType() {
+        return getAppContext().getResources().getString(R.string.account_type);
+    }
+    
+    //  From AccountAuthenticator 
+    //  public static final String AUTHORITY = "org.owncloud";
+    public static String getAuthority() {
+        return getAppContext().getResources().getString(R.string.authority);
+    }
+    
+    //  From AccountAuthenticator
+    //  public static final String AUTH_TOKEN_TYPE = "org.owncloud";
+    public static String getAuthTokenType() {
+        return getAppContext().getResources().getString(R.string.authority);
+    }
+    
+    //  From AccountAuthenticator
+    //  public static final String AUTH_TOKEN_TYPE_PASSWORD = "owncloud.password";
+    public static String getAuthTokenTypePass() {
+        return getAppContext().getResources().getString(R.string.account_type) + ".password";
+    }
+    
+    //  From AccountAuthenticator
+    //  public static final String AUTH_TOKEN_TYPE_ACCESS_TOKEN = "owncloud.oauth2.access_token";
+    public static String getAuthTokenTypeAccessToken() {
+        return getAppContext().getResources().getString(R.string.account_type) + ".oauth2.access_token";
+    }
+    
+    //  From AccountAuthenticator
+    //  public static final String AUTH_TOKEN_TYPE_REFRESH_TOKEN = "owncloud.oauth2.refresh_token";
+    public static String getAuthTokenTypeRefreshToken() {
+        return getAppContext().getResources().getString(R.string.account_type) + ".oauth2.refresh_token";
+    }
+    
+    //  From AccountAuthenticator
+    //  public static final String AUTH_TOKEN_TYPE_SAML_WEB_SSO_SESSION_COOKIE = "owncloud.saml.web_sso.session_cookie";
+    public static String getAuthTokenTypeSamlSessionCookie() {
+        return getAppContext().getResources().getString(R.string.account_type) +  ".saml.web_sso.session_cookie";
+    }
+    
+    //  From ProviderMeta 
+    //  public static final String DB_FILE = "owncloud.db";
+    public static String getDBFile() {
+        return getAppContext().getResources().getString(R.string.db_file);
+    }
+    
+    //  From ProviderMeta
+    //  private final String mDatabaseName = "ownCloud";
+    public static String getDBName() {
+        return getAppContext().getResources().getString(R.string.db_name);
+    }
+     
+    //  data_folder
+    public static String getDataFolder() {
+        return getAppContext().getResources().getString(R.string.data_folder);
+    }
+    
+    // log_name
+    public static String getLogName() {
+        return getAppContext().getResources().getString(R.string.log_name);
+    }
+}
diff --git a/src/com/owncloud/android/OwnCloudSession.java b/src/com/owncloud/android/OwnCloudSession.java
new file mode 100644 (file)
index 0000000..d7bb609
--- /dev/null
@@ -0,0 +1,56 @@
+/* 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 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
+package com.owncloud.android;\r
+\r
+/**\r
+ * Represents a session to an ownCloud instance\r
+ * \r
+ * @author Bartek Przybylski\r
+ * \r
+ */\r
+public class OwnCloudSession {\r
+    private String mSessionName;\r
+    private String mSessionUrl;\r
+    private int mEntryId;\r
+\r
+    public OwnCloudSession(String name, String url, int entryId) {\r
+        mSessionName = name;\r
+        mSessionUrl = url;\r
+        mEntryId = entryId;\r
+    }\r
+\r
+    public void setName(String name) {\r
+        mSessionName = name;\r
+    }\r
+\r
+    public String getName() {\r
+        return mSessionName;\r
+    }\r
+\r
+    public void setUrl(String url) {\r
+        mSessionUrl = url;\r
+    }\r
+\r
+    public String getUrl() {\r
+        return mSessionUrl;\r
+    }\r
+\r
+    public int getEntryId() {\r
+        return mEntryId;\r
+    }\r
+}\r
diff --git a/src/com/owncloud/android/Uploader.java b/src/com/owncloud/android/Uploader.java
new file mode 100644 (file)
index 0000000..ddd3f4c
--- /dev/null
@@ -0,0 +1,429 @@
+/* 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.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.FileUploader;
+import com.owncloud.android.ui.CustomButton;
+
+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.Audio;
+import android.provider.MediaStore.Images;
+import android.provider.MediaStore.Video;
+import android.view.View;
+import android.view.Window;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.EditText;
+import android.widget.SimpleAdapter;
+import android.widget.Toast;
+
+
+
+/**
+ * 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 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 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 (prepareStreamsToUpload()) {
+            mAccountManager = (AccountManager) getSystemService(Context.ACCOUNT_SERVICE);
+            Account[] accounts = mAccountManager.getAccountsByType(MainApp.getAccountType());
+            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[] { MainApp.getAuthTokenType() });
+                        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_MULTIPLE_ACCOUNT:
+            CharSequence ac[] = new CharSequence[mAccountManager.getAccountsByType(MainApp.getAccountType()).length];
+            for (int i = 0; i < ac.length; ++i) {
+                ac[i] = mAccountManager.getAccountsByType(MainApp.getAccountType())[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(MainApp.getAccountType())[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;
+        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(MainApp.getAuthTokenType());
+            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);
+            CustomButton btn = (CustomButton) 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 {
+            //WebdavClient webdav = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext());
+
+            ArrayList<String> local = new ArrayList<String>();
+            ArrayList<String> remote = new ArrayList<String>();
+            
+            /* 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);
+            }
+            */
+            
+            // this checks the mimeType 
+            for (Parcelable mStream : mStreamsToUpload) {
+                
+                Uri uri = (Uri) mStream;
+                if (uri !=null) {
+                    if (uri.getScheme().equals("content")) {
+                        
+                       String mimeType = getContentResolver().getType(uri);
+                       
+                       if (mimeType.contains("image")) {
+                           String[] CONTENT_PROJECTION = { Images.Media.DATA, Images.Media.DISPLAY_NAME, Images.Media.MIME_TYPE, Images.Media.SIZE};
+                           Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null, null, null);
+                           c.moveToFirst();
+                           int index = c.getColumnIndex(Images.Media.DATA);
+                           String data = c.getString(index);
+                           local.add(data);
+                           remote.add(mUploadPath + c.getString(c.getColumnIndex(Images.Media.DISPLAY_NAME)));
+                       
+                       }
+                       else if (mimeType.contains("video")) {
+                           String[] CONTENT_PROJECTION = { Video.Media.DATA, Video.Media.DISPLAY_NAME, Video.Media.MIME_TYPE, Video.Media.SIZE, Video.Media.DATE_MODIFIED };
+                           Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null, null, null);
+                           c.moveToFirst();
+                           int index = c.getColumnIndex(Video.Media.DATA);
+                           String data = c.getString(index);
+                           local.add(data);
+                           remote.add(mUploadPath + c.getString(c.getColumnIndex(Video.Media.DISPLAY_NAME)));
+                          
+                       }
+                       else if (mimeType.contains("audio")) {
+                           String[] CONTENT_PROJECTION = { Audio.Media.DATA, Audio.Media.DISPLAY_NAME, Audio.Media.MIME_TYPE, Audio.Media.SIZE };
+                           Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null, null, null);
+                           c.moveToFirst();
+                           int index = c.getColumnIndex(Audio.Media.DATA);
+                           String data = c.getString(index);
+                           local.add(data);
+                           remote.add(mUploadPath + c.getString(c.getColumnIndex(Audio.Media.DISPLAY_NAME)));
+                        
+                       }
+                       else {
+                           String filePath = Uri.decode(uri.toString()).replace(uri.getScheme() + "://", "");
+                           // cut everything whats before mnt. It occured to me that sometimes apps send their name into the URI
+                           if (filePath.contains("mnt")) {
+                              String splitedFilePath[] = filePath.split("/mnt");
+                              filePath = splitedFilePath[1];
+                           }
+                           final File file = new File(filePath);
+                           local.add(file.getAbsolutePath());
+                           remote.add(mUploadPath + file.getName());
+                       }
+                        
+                    } else if (uri.getScheme().equals("file")) {
+                        String filePath = Uri.decode(uri.toString()).replace(uri.getScheme() + "://", "");
+                        if (filePath.contains("mnt")) {
+                           String splitedFilePath[] = filePath.split("/mnt");
+                           filePath = splitedFilePath[1];
+                        }
+                        final File file = new File(filePath);
+                        local.add(file.getAbsolutePath());
+                        remote.add(mUploadPath + file.getName());
+                    }
+                    else {
+                        throw new SecurityException();
+                    }
+                }
+                else {
+                    throw new SecurityException();
+                }
+           
+            Intent intent = new Intent(getApplicationContext(), FileUploader.class);
+            intent.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES);
+            intent.putExtra(FileUploader.KEY_LOCAL_FILE, local.toArray(new String[local.size()]));
+            intent.putExtra(FileUploader.KEY_REMOTE_FILE, remote.toArray(new String[remote.size()]));
+            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();            
+        }
+    }
+
+}
diff --git a/src/com/owncloud/android/authentication/AccountAuthenticator.java b/src/com/owncloud/android/authentication/AccountAuthenticator.java
new file mode 100644 (file)
index 0000000..a2a38e8
--- /dev/null
@@ -0,0 +1,359 @@
+/* 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 com.owncloud.android.Log_OC;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
+
+import android.accounts.*;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.widget.Toast;
+
+
+
+
+/**
+ *  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.
+     */
+    /* These constants are now in MainApp
+         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 AUTH_TOKEN_TYPE_SAML_WEB_SSO_SESSION_COOKIE = "owncloud.saml.web_sso.session_cookie";
+    */
+    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";
+    /**
+     * Flag signaling if the ownCloud server can be accessed with session cookies from SAML-based web single-sign-on.
+     */
+    public static final String KEY_SUPPORTS_SAML_WEB_SSO = "oc_supports_saml_web_sso";
+    
+    private static final String TAG = AccountAuthenticator.class.getSimpleName();
+    
+    private Context mContext;
+    
+    private Handler mHandler;
+
+    public AccountAuthenticator(Context context) {
+        super(context);
+        mContext = context;
+        mHandler = new Handler();
+    }
+
+    /**
+     * {@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);
+        
+        final Bundle bundle = new Bundle();
+        
+        AccountManager accountManager = AccountManager.get(mContext);
+        Account[] accounts = accountManager.getAccountsByType(MainApp.getAccountType());
+        
+        if (mContext.getResources().getBoolean(R.bool.multiaccount_support) || accounts.length < 1) {
+            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);
+            
+            bundle.putParcelable(AccountManager.KEY_INTENT, intent);
+        
+        } else {
+
+            // Return an error
+            bundle.putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION);
+            final String message = String.format(mContext.getString(R.string.auth_unsupported_multiaccount), mContext.getString(R.string.app_name)); 
+            bundle.putString(AccountManager.KEY_ERROR_MESSAGE, message);
+           
+            mHandler.post(new Runnable() {
+
+                @Override
+                public void run() {
+                    Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
+                }
+            });
+            
+        }
+        
+        return bundle;
+    }
+
+    /**\r
+     * {@inheritDoc}\r
+     */\r
+    @Override\r
+    public Bundle confirmCredentials(AccountAuthenticatorResponse response,\r
+            Account account, Bundle options) throws NetworkErrorException {\r
+        try {\r
+            validateAccountType(account.type);\r
+        } catch (AuthenticatorException e) {\r
+            Log_OC.e(TAG, "Failed to validate account type " + account.type + ": "\r
+                    + e.getMessage());\r
+            e.printStackTrace();\r
+            return e.getFailureBundle();\r
+        }\r
+        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_LOGIN_OPTIONS, options);\r
+\r
+        setIntentFlags(intent);\r
+\r
+        Bundle resultBundle = new Bundle();\r
+        resultBundle.putParcelable(AccountManager.KEY_INTENT, intent);\r
+        return resultBundle;\r
+    }\r
+\r
+    @Override\r
+    public Bundle editProperties(AccountAuthenticatorResponse response,\r
+            String accountType) {\r
+        return null;\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(MainApp.getAuthTokenTypePass())) {
+            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, MainApp.getAccountType());
+            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_ENFORCED_UPDATE, true);
+        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(MainApp.getAccountType())) {
+            throw new UnsupportedAccountTypeException();
+        }
+    }
+
+    private void validateAuthTokenType(String authTokenType)\r
+            throws UnsupportedAuthTokenTypeException {\r
+        if (!authTokenType.equals(MainApp.getAuthTokenType()) &&\r
+            !authTokenType.equals(MainApp.getAuthTokenTypePass()) &&\r
+            !authTokenType.equals(MainApp.getAuthTokenTypeAccessToken()) &&\r
+            !authTokenType.equals(MainApp.getAuthTokenTypeRefreshToken()) &&
+            !authTokenType.equals(MainApp.getAuthTokenTypeSamlSessionCookie())) {\r
+            throw new UnsupportedAuthTokenTypeException();\r
+        }\r
+    }\r
+\r
+    public static class AuthenticatorException extends Exception {\r
+        private static final long serialVersionUID = 1L;\r
+        private Bundle mFailureBundle;\r
+\r
+        public AuthenticatorException(int code, String errorMsg) {\r
+            mFailureBundle = new Bundle();\r
+            mFailureBundle.putInt(AccountManager.KEY_ERROR_CODE, code);\r
+            mFailureBundle\r
+                    .putString(AccountManager.KEY_ERROR_MESSAGE, errorMsg);\r
+        }\r
+\r
+        public Bundle getFailureBundle() {\r
+            return mFailureBundle;\r
+        }\r
+    }\r
+\r
+    public static class UnsupportedAccountTypeException extends\r
+            AuthenticatorException {\r
+        private static final long serialVersionUID = 1L;\r
+\r
+        public UnsupportedAccountTypeException() {\r
+            super(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,\r
+                    "Unsupported account type");\r
+        }\r
+    }\r
+\r
+    public static class UnsupportedAuthTokenTypeException extends\r
+            AuthenticatorException {\r
+        private static final long serialVersionUID = 1L;\r
+\r
+        public UnsupportedAuthTokenTypeException() {\r
+            super(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,\r
+                    "Unsupported auth token type");\r
+        }\r
+    }\r
+\r
+    public static class UnsupportedFeaturesException extends\r
+            AuthenticatorException {\r
+        public static final long serialVersionUID = 1L;\r
+\r
+        public UnsupportedFeaturesException() {\r
+            super(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,\r
+                    "Unsupported features");\r
+        }\r
+    }\r
+\r
+    public static class AccessDeniedException extends AuthenticatorException {\r
+        public AccessDeniedException(int code, String errorMsg) {\r
+            super(AccountManager.ERROR_CODE_INVALID_RESPONSE, "Access Denied");\r
+        }\r
+\r
+        private static final long serialVersionUID = 1L;\r
+\r
+    }\r
+}\r
diff --git a/src/com/owncloud/android/authentication/AccountAuthenticatorActivity.java b/src/com/owncloud/android/authentication/AccountAuthenticatorActivity.java
new file mode 100644 (file)
index 0000000..62c8825
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.owncloud.android.authentication;
+
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.AccountManager;
+import android.os.Bundle;
+
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+
+
+/*
+ * Base class for implementing an Activity that is used to help implement an AbstractAccountAuthenticator. 
+ * If the AbstractAccountAuthenticator needs to use an activity to handle the request then it can have the activity extend 
+ * AccountAuthenticatorActivity. The AbstractAccountAuthenticator passes in the response to the intent using the following:
+ * intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
+ * 
+ * The activity then sets the result that is to be handed to the response via setAccountAuthenticatorResult(android.os.Bundle). 
+ * This result will be sent as the result of the request when the activity finishes. If this is never set or if it is set to null 
+ * then error AccountManager.ERROR_CODE_CANCELED will be called on the response.
+ */
+
+public class AccountAuthenticatorActivity extends SherlockFragmentActivity {
+
+    private AccountAuthenticatorResponse mAccountAuthenticatorResponse = null;
+    private Bundle mResultBundle = null;
+
+
+    /**
+     * Set the result that is to be sent as the result of the request that caused this Activity to be launched.
+     * If result is null or this method is never called then the request will be canceled.
+     * 
+     * @param result this is returned as the result of the AbstractAccountAuthenticator request
+     */
+    public final void setAccountAuthenticatorResult(Bundle result) {
+        mResultBundle = result;
+    }
+
+    /**
+     * Retreives the AccountAuthenticatorResponse from either the intent of the icicle, if the
+     * icicle is non-zero.
+     * @param icicle the save instance data of this Activity, may be null
+     */
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        mAccountAuthenticatorResponse =
+                getIntent().getParcelableExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
+
+        if (mAccountAuthenticatorResponse != null) {
+            mAccountAuthenticatorResponse.onRequestContinued();
+        }
+    }
+    
+    /**
+     * Sends the result or a Constants.ERROR_CODE_CANCELED error if a result isn't present.
+     */
+    public void finish() {
+        if (mAccountAuthenticatorResponse != null) {
+            // send the result bundle back if set, otherwise send an error.
+            if (mResultBundle != null) {
+                mAccountAuthenticatorResponse.onResult(mResultBundle);
+            } else {
+                mAccountAuthenticatorResponse.onError(AccountManager.ERROR_CODE_CANCELED,
+                        "canceled");
+            }
+            mAccountAuthenticatorResponse = null;
+        }
+        super.finish();
+    }
+}
diff --git a/src/com/owncloud/android/authentication/AccountAuthenticatorService.java b/src/com/owncloud/android/authentication/AccountAuthenticatorService.java
new file mode 100644 (file)
index 0000000..4c91f6e
--- /dev/null
@@ -0,0 +1,41 @@
+/* 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.authentication;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+public class AccountAuthenticatorService extends Service {
+
+    private AccountAuthenticator mAuthenticator;
+    //static final public String ACCOUNT_TYPE = "owncloud";
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mAuthenticator = new AccountAuthenticator(this);
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mAuthenticator.getIBinder();
+    }
+
+}
diff --git a/src/com/owncloud/android/authentication/AccountUtils.java b/src/com/owncloud/android/authentication/AccountUtils.java
new file mode 100644 (file)
index 0000000..3b79c39
--- /dev/null
@@ -0,0 +1,220 @@
+/* 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 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.authentication;\r
+\r
+import com.owncloud.android.MainApp;\r
+import com.owncloud.android.utils.OwnCloudVersion;\r
+\r
+import android.accounts.Account;\r
+import android.accounts.AccountManager;\r
+import android.accounts.AccountsException;\r
+import android.content.Context;\r
+import android.content.SharedPreferences;\r
+import android.preference.PreferenceManager;\r
+\r
+public class AccountUtils {\r
+    public static final String WEBDAV_PATH_1_2 = "/webdav/owncloud.php";\r
+    public static final String WEBDAV_PATH_2_0 = "/files/webdav.php";\r
+    public static final String WEBDAV_PATH_4_0 = "/remote.php/webdav";\r
+    private static final String ODAV_PATH = "/remote.php/odav";\r
+    private static final String SAML_SSO_PATH = "/remote.php/webdav";\r
+    public static final String CARDDAV_PATH_2_0 = "/apps/contacts/carddav.php";\r
+    public static final String CARDDAV_PATH_4_0 = "/remote/carddav.php";\r
+    public static final String STATUS_PATH = "/status.php";\r
+\r
+    /**\r
+     * Can be used to get the currently selected ownCloud {@link Account} in the\r
+     * application preferences.\r
+     * \r
+     * @param   context     The current application {@link Context}\r
+     * @return              The ownCloud {@link Account} currently saved in preferences, or the first \r
+     *                      {@link Account} available, if valid (still registered in the system as ownCloud \r
+     *                      account). If none is available and valid, returns null.\r
+     */\r
+    public static Account getCurrentOwnCloudAccount(Context context) {\r
+        Account[] ocAccounts = AccountManager.get(context).getAccountsByType(\r
+                MainApp.getAccountType());\r
+        Account defaultAccount = null;\r
+\r
+        SharedPreferences appPreferences = PreferenceManager\r
+                .getDefaultSharedPreferences(context);\r
+        String accountName = appPreferences\r
+                .getString("select_oc_account", null);\r
+\r
+        // account validation: the saved account MUST be in the list of ownCloud Accounts known by the AccountManager\r
+        if (accountName != null) {\r
+            for (Account account : ocAccounts) {\r
+                if (account.name.equals(accountName)) {\r
+                    defaultAccount = account;\r
+                    break;\r
+                }\r
+            }\r
+        }\r
+        \r
+        if (defaultAccount == null && ocAccounts.length != 0) {\r
+            // take first account as fallback\r
+            defaultAccount = ocAccounts[0];\r
+        }\r
+\r
+        return defaultAccount;\r
+    }\r
+\r
+    \r
+    public static boolean exists(Account account, Context context) {\r
+        Account[] ocAccounts = AccountManager.get(context).getAccountsByType(\r
+                MainApp.getAccountType());\r
+\r
+        if (account != null && account.name != null) {\r
+            for (Account ac : ocAccounts) {\r
+                if (ac.name.equals(account.name)) {\r
+                    return true;\r
+                }\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+    \r
+\r
+    /**\r
+     * Checks, whether or not there are any ownCloud accounts setup.\r
+     * \r
+     * @return true, if there is at least one account.\r
+     */\r
+    public static boolean accountsAreSetup(Context context) {\r
+        AccountManager accMan = AccountManager.get(context);\r
+        Account[] accounts = accMan\r
+                .getAccountsByType(MainApp.getAccountType());\r
+        return accounts.length > 0;\r
+    }\r
+    \r
+    \r
+    public static boolean setCurrentOwnCloudAccount(Context context, String accountName) {\r
+        boolean result = false;\r
+        if (accountName != null) {\r
+            Account[] ocAccounts = AccountManager.get(context).getAccountsByType(\r
+                    MainApp.getAccountType());\r
+            boolean found = false;\r
+            for (Account account : ocAccounts) {\r
+                found = (account.name.equals(accountName));\r
+                if (found) {\r
+                    SharedPreferences.Editor appPrefs = PreferenceManager\r
+                            .getDefaultSharedPreferences(context).edit();\r
+                    appPrefs.putString("select_oc_account", accountName);\r
+    \r
+                    appPrefs.commit();\r
+                    result = true;\r
+                    break;\r
+                }\r
+            }\r
+        }\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * \r
+     * @param version version of owncloud\r
+     * @return webdav path for given OC version, null if OC version unknown\r
+     */\r
+    public static String getWebdavPath(OwnCloudVersion version, boolean supportsOAuth, boolean supportsSamlSso) {\r
+        if (version != null) {\r
+            if (supportsOAuth) {\r
+                return ODAV_PATH;\r
+            }\r
+            if (supportsSamlSso) {\r
+                return SAML_SSO_PATH;\r
+            }\r
+            if (version.compareTo(OwnCloudVersion.owncloud_v4) >= 0)\r
+                return WEBDAV_PATH_4_0;\r
+            if (version.compareTo(OwnCloudVersion.owncloud_v3) >= 0\r
+                    || version.compareTo(OwnCloudVersion.owncloud_v2) >= 0)\r
+                return WEBDAV_PATH_2_0;\r
+            if (version.compareTo(OwnCloudVersion.owncloud_v1) >= 0)\r
+                return WEBDAV_PATH_1_2;\r
+        }\r
+        return null;\r
+    }\r
+    \r
+    /**\r
+     * Returns the proper URL path to access the WebDAV interface of an ownCloud server,\r
+     * according to its version and the authorization method used.\r
+     * \r
+     * @param   version         Version of ownCloud server.\r
+     * @param   authTokenType   Authorization token type, matching some of the AUTH_TOKEN_TYPE_* constants in {@link AccountAuthenticator}. \r
+     * @return                  WebDAV path for given OC version and authorization method, null if OC version is unknown.\r
+     */\r
+    public static String getWebdavPath(OwnCloudVersion version, String authTokenType) {\r
+        if (version != null) {\r
+            if (MainApp.getAuthTokenTypeAccessToken().equals(authTokenType)) {\r
+                return ODAV_PATH;\r
+            }\r
+            if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(authTokenType)) {\r
+                return SAML_SSO_PATH;\r
+            }\r
+            if (version.compareTo(OwnCloudVersion.owncloud_v4) >= 0)\r
+                return WEBDAV_PATH_4_0;\r
+            if (version.compareTo(OwnCloudVersion.owncloud_v3) >= 0\r
+                    || version.compareTo(OwnCloudVersion.owncloud_v2) >= 0)\r
+                return WEBDAV_PATH_2_0;\r
+            if (version.compareTo(OwnCloudVersion.owncloud_v1) >= 0)\r
+                return WEBDAV_PATH_1_2;\r
+        }\r
+        return null;\r
+    }\r
+    \r
+    /**\r
+     * Constructs full url to host and webdav resource basing on host version\r
+     * @param context\r
+     * @param account\r
+     * @return url or null on failure\r
+     * @throws AccountNotFoundException     When 'account' is unknown for the AccountManager\r
+     */\r
+    public static String constructFullURLForAccount(Context context, Account account) throws AccountNotFoundException {\r
+        AccountManager ama = AccountManager.get(context);\r
+        String baseurl = ama.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL);\r
+        String strver  = ama.getUserData(account, AccountAuthenticator.KEY_OC_VERSION);\r
+        boolean supportsOAuth = (ama.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null);\r
+        boolean supportsSamlSso = (ama.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO) != null);\r
+        OwnCloudVersion ver = new OwnCloudVersion(strver);\r
+        String webdavpath = getWebdavPath(ver, supportsOAuth, supportsSamlSso);\r
+\r
+        if (baseurl == null || webdavpath == null) \r
+            throw new AccountNotFoundException(account, "Account not found", null);\r
+        \r
+        return baseurl + webdavpath;\r
+    }\r
+    \r
+    \r
+    public static class AccountNotFoundException extends AccountsException {\r
+        \r
+        /** Generated - should be refreshed every time the class changes!! */\r
+        private static final long serialVersionUID = -9013287181793186830L;\r
+        \r
+        private Account mFailedAccount; \r
+                \r
+        public AccountNotFoundException(Account failedAccount, String message, Throwable cause) {\r
+            super(message, cause);\r
+            mFailedAccount = failedAccount;\r
+        }\r
+        \r
+        public Account getFailedAccount() {\r
+            return mFailedAccount;\r
+        }\r
+    }\r
+\r
+}\r
diff --git a/src/com/owncloud/android/authentication/AuthenticatorActivity.java b/src/com/owncloud/android/authentication/AuthenticatorActivity.java
new file mode 100644 (file)
index 0000000..69872d7
--- /dev/null
@@ -0,0 +1,1672 @@
+/* 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 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.authentication;\r
+\r
+import android.accounts.Account;\r
+import android.accounts.AccountManager;\r
+import android.app.AlertDialog;\r
+import android.app.Dialog;\r
+import android.app.ProgressDialog;\r
+import android.content.ContentResolver;\r
+import android.content.DialogInterface;\r
+import android.content.Intent;\r
+import android.content.SharedPreferences;\r
+import android.graphics.Rect;\r
+import android.graphics.drawable.Drawable;\r
+import android.net.Uri;\r
+import android.os.Bundle;\r
+import android.os.Handler;\r
+import android.preference.PreferenceManager;\r
+import android.support.v4.app.Fragment;\r
+import android.text.Editable;\r
+import android.text.InputType;\r
+import android.text.TextWatcher;\r
+import android.view.KeyEvent;\r
+import android.view.MotionEvent;\r
+import android.view.View;\r
+import android.view.View.OnFocusChangeListener;\r
+import android.view.View.OnTouchListener;\r
+import android.view.Window;\r
+import android.view.inputmethod.EditorInfo;\r
+import android.widget.Button;\r
+import android.widget.CheckBox;\r
+import android.widget.EditText;\r
+import android.widget.TextView;\r
+import android.widget.TextView.OnEditorActionListener;\r
+\r
+import com.actionbarsherlock.app.SherlockDialogFragment;\r
+import com.owncloud.android.Log_OC;\r
+import com.owncloud.android.MainApp;\r
+import com.owncloud.android.R;\r
+import com.owncloud.android.authentication.SsoWebViewClient.SsoWebViewClientListener;\r
+import com.owncloud.android.network.OwnCloudClientUtils;\r
+import com.owncloud.android.operations.ExistenceCheckOperation;\r
+import com.owncloud.android.operations.OAuth2GetAccessToken;\r
+import com.owncloud.android.operations.OnRemoteOperationListener;\r
+import com.owncloud.android.operations.OwnCloudServerCheckOperation;\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.ui.CustomButton;\r
+import com.owncloud.android.ui.dialog.SamlWebViewDialog;\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
+\r
+\r
+import eu.alefzero.webdav.WebdavClient;\r
+\r
+/**\r
+ * This Activity is used to add an ownCloud account to the App\r
+ * \r
+ * @author Bartek Przybylski\r
+ * @author David A. Velasco\r
+ */\r
+public class AuthenticatorActivity extends AccountAuthenticatorActivity\r
+implements  OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeListener, OnEditorActionListener, SsoWebViewClientListener{\r
+\r
+    private static final String TAG = AuthenticatorActivity.class.getSimpleName();\r
+\r
+    public static final String EXTRA_ACCOUNT = "ACCOUNT";\r
+    public static final String EXTRA_USER_NAME = "USER_NAME";\r
+    public static final String EXTRA_HOST_NAME = "HOST_NAME";\r
+    public static final String EXTRA_ACTION = "ACTION";\r
+    public static final String EXTRA_ENFORCED_UPDATE = "ENFORCE_UPDATE";\r
+\r
+    private static final String KEY_AUTH_MESSAGE_VISIBILITY = "AUTH_MESSAGE_VISIBILITY";\r
+    private static final String KEY_AUTH_MESSAGE_TEXT = "AUTH_MESSAGE_TEXT";\r
+    private static final String KEY_HOST_URL_TEXT = "HOST_URL_TEXT";\r
+    private static final String KEY_OC_VERSION = "OC_VERSION";\r
+    private static final String KEY_ACCOUNT = "ACCOUNT";\r
+    private static final String KEY_SERVER_VALID = "SERVER_VALID";\r
+    private static final String KEY_SERVER_CHECKED = "SERVER_CHECKED";\r
+    private static final String KEY_SERVER_CHECK_IN_PROGRESS = "SERVER_CHECK_IN_PROGRESS"; \r
+    private static final String KEY_SERVER_STATUS_TEXT = "SERVER_STATUS_TEXT";\r
+    private static final String KEY_SERVER_STATUS_ICON = "SERVER_STATUS_ICON";\r
+    private static final String KEY_IS_SSL_CONN = "IS_SSL_CONN";\r
+    private static final String KEY_PASSWORD_VISIBLE = "PASSWORD_VISIBLE";\r
+    private static final String KEY_AUTH_STATUS_TEXT = "AUTH_STATUS_TEXT";\r
+    private static final String KEY_AUTH_STATUS_ICON = "AUTH_STATUS_ICON";\r
+    private static final String KEY_REFRESH_BUTTON_ENABLED = "KEY_REFRESH_BUTTON_ENABLED";\r
+    \r
+    private static final String KEY_OC_USERNAME_EQUALS = "oc_username=";\r
+\r
+    private static final String AUTH_ON = "on";\r
+    private static final String AUTH_OFF = "off";\r
+    private static final String AUTH_OPTIONAL = "optional";\r
+    \r
+    private static final int DIALOG_LOGIN_PROGRESS = 0;\r
+    private static final int DIALOG_SSL_VALIDATOR = 1;\r
+    private static final int DIALOG_CERT_NOT_SAVED = 2;\r
+    private static final int DIALOG_OAUTH2_LOGIN_PROGRESS = 3;\r
+\r
+    public static final byte ACTION_CREATE = 0;\r
+    public static final byte ACTION_UPDATE_TOKEN = 1;\r
+\r
+    private static final String TAG_SAML_DIALOG = "samlWebViewDialog";\r
+    \r
+    private String mHostBaseUrl;\r
+    private OwnCloudVersion mDiscoveredVersion;\r
+\r
+    private String mAuthMessageText;\r
+    private int mAuthMessageVisibility, mServerStatusText, mServerStatusIcon;\r
+    private boolean mServerIsChecked, mServerIsValid, mIsSslConn;\r
+    private int mAuthStatusText, mAuthStatusIcon;    \r
+    private TextView mAuthStatusLayout;\r
+\r
+    private final Handler mHandler = new Handler();\r
+    private Thread mOperationThread;\r
+    private OwnCloudServerCheckOperation mOcServerChkOperation;\r
+    private ExistenceCheckOperation mAuthCheckOperation;\r
+    private RemoteOperationResult mLastSslUntrustedServerResult;\r
+\r
+    private Uri mNewCapturedUriFromOAuth2Redirection;\r
+\r
+    private AccountManager mAccountMgr;\r
+    private boolean mJustCreated;\r
+    private byte mAction;\r
+    private Account mAccount;\r
+\r
+    private TextView mAuthMessage;\r
+    \r
+    private EditText mHostUrlInput;\r
+    private boolean mHostUrlInputEnabled;\r
+    private View mRefreshButton;\r
+\r
+    private String mAuthTokenType;\r
+    \r
+    private EditText mUsernameInput;\r
+    private EditText mPasswordInput;\r
+    \r
+    private CheckBox mOAuth2Check;\r
+    \r
+    private TextView mOAuthAuthEndpointText;\r
+    private TextView mOAuthTokenEndpointText;\r
+    \r
+    private SamlWebViewDialog mSamlDialog;\r
+    \r
+    private View mOkButton;\r
+    \r
+    private String mAuthToken;\r
+    \r
+    private boolean mResumed; // Control if activity is resumed\r
+\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * \r
+     * IMPORTANT ENTRY POINT 1: activity is shown to the user\r
+     */\r
+    @Override\r
+    protected void onCreate(Bundle savedInstanceState) {\r
+        super.onCreate(savedInstanceState);\r
+        getWindow().requestFeature(Window.FEATURE_NO_TITLE);\r
+\r
+        /// set view and get references to view elements\r
+        setContentView(R.layout.account_setup);\r
+        mAuthMessage = (TextView) findViewById(R.id.auth_message);\r
+        mHostUrlInput = (EditText) findViewById(R.id.hostUrlInput);\r
+        mHostUrlInput.setText(getString(R.string.server_url));  // valid although R.string.server_url is an empty string\r
+        mUsernameInput = (EditText) findViewById(R.id.account_username);\r
+        mPasswordInput = (EditText) findViewById(R.id.account_password);\r
+        mOAuthAuthEndpointText = (TextView)findViewById(R.id.oAuthEntryPoint_1);\r
+        mOAuthTokenEndpointText = (TextView)findViewById(R.id.oAuthEntryPoint_2);\r
+        mOAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check);\r
+        mOkButton = (CustomButton) findViewById(R.id.buttonOK);\r
+        mAuthStatusLayout = (TextView) findViewById(R.id.auth_status_text); \r
+        \r
+        /// set Host Url Input Enabled\r
+        mHostUrlInputEnabled = getResources().getBoolean(R.bool.show_server_url_input);\r
+        \r
+\r
+        /// complete label for 'register account' button\r
+        Button b = (Button) findViewById(R.id.account_register);\r
+        if (b != null) {\r
+            b.setText(String.format(getString(R.string.auth_register), getString(R.string.app_name)));            \r
+        }\r
+\r
+//        /// complete background of 'OK' button\r
+//        boolean customButtons = getResources().getBoolean(R.bool.custom_buttons);\r
+//        if (customButtons)\r
+//            mOkButton.setBackgroundResource(R.drawable.btn_default);\r
+        \r
+        /// initialization\r
+        mAccountMgr = AccountManager.get(this);\r
+        mNewCapturedUriFromOAuth2Redirection = null;\r
+        mAction = getIntent().getByteExtra(EXTRA_ACTION, ACTION_CREATE); \r
+        mAccount = null;\r
+        mHostBaseUrl = "";\r
+        boolean refreshButtonEnabled = false;\r
+        \r
+        // URL input configuration applied\r
+        if (!mHostUrlInputEnabled)\r
+        {\r
+            findViewById(R.id.hostUrlFrame).setVisibility(View.GONE);\r
+            mRefreshButton = findViewById(R.id.centeredRefreshButton);\r
+\r
+        } else {\r
+            mRefreshButton = findViewById(R.id.embeddedRefreshButton);\r
+        }\r
+\r
+        if (savedInstanceState == null) {\r
+            mResumed = false;\r
+            /// connection state and info\r
+            mAuthMessageVisibility = View.GONE;\r
+            mServerStatusText = mServerStatusIcon = 0;\r
+            mServerIsValid = false;\r
+            mServerIsChecked = false;\r
+            mIsSslConn = false;\r
+            mAuthStatusText = mAuthStatusIcon = 0;\r
+\r
+            /// retrieve extras from intent\r
+            mAccount = getIntent().getExtras().getParcelable(EXTRA_ACCOUNT);\r
+            if (mAccount != null) {\r
+                String ocVersion = mAccountMgr.getUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION);\r
+                if (ocVersion != null) {\r
+                    mDiscoveredVersion = new OwnCloudVersion(ocVersion);\r
+                }\r
+                mHostBaseUrl = normalizeUrl(mAccountMgr.getUserData(mAccount, AccountAuthenticator.KEY_OC_BASE_URL));\r
+                mHostUrlInput.setText(mHostBaseUrl);\r
+                String userName = mAccount.name.substring(0, mAccount.name.lastIndexOf('@'));\r
+                mUsernameInput.setText(userName);\r
+            }\r
+            initAuthorizationMethod();  // checks intent and setup.xml to determine mCurrentAuthorizationMethod\r
+            mJustCreated = true;\r
+            \r
+            if (mAction == ACTION_UPDATE_TOKEN || !mHostUrlInputEnabled) {\r
+                checkOcServer(); \r
+            }\r
+            \r
+        } else {\r
+            mResumed = true;\r
+            /// connection state and info\r
+            mAuthMessageVisibility = savedInstanceState.getInt(KEY_AUTH_MESSAGE_VISIBILITY);\r
+            mAuthMessageText = savedInstanceState.getString(KEY_AUTH_MESSAGE_TEXT);\r
+            mServerIsValid = savedInstanceState.getBoolean(KEY_SERVER_VALID);\r
+            mServerIsChecked = savedInstanceState.getBoolean(KEY_SERVER_CHECKED);\r
+            mServerStatusText = savedInstanceState.getInt(KEY_SERVER_STATUS_TEXT);\r
+            mServerStatusIcon = savedInstanceState.getInt(KEY_SERVER_STATUS_ICON);\r
+            mIsSslConn = savedInstanceState.getBoolean(KEY_IS_SSL_CONN);\r
+            mAuthStatusText = savedInstanceState.getInt(KEY_AUTH_STATUS_TEXT);\r
+            mAuthStatusIcon = savedInstanceState.getInt(KEY_AUTH_STATUS_ICON);\r
+            if (savedInstanceState.getBoolean(KEY_PASSWORD_VISIBLE, false)) {\r
+                showPassword();\r
+            }\r
+            \r
+            /// server data\r
+            String ocVersion = savedInstanceState.getString(KEY_OC_VERSION);\r
+            if (ocVersion != null) {\r
+                mDiscoveredVersion = new OwnCloudVersion(ocVersion);\r
+            }\r
+            mHostBaseUrl = savedInstanceState.getString(KEY_HOST_URL_TEXT);\r
+\r
+            // account data, if updating\r
+            mAccount = savedInstanceState.getParcelable(KEY_ACCOUNT);\r
+            mAuthTokenType = savedInstanceState.getString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE);\r
+            if (mAuthTokenType == null) {\r
+                mAuthTokenType =  MainApp.getAuthTokenTypePass();\r
+                \r
+            }\r
+\r
+            // check if server check was interrupted by a configuration change\r
+            if (savedInstanceState.getBoolean(KEY_SERVER_CHECK_IN_PROGRESS, false)) {\r
+                checkOcServer();\r
+            }            \r
+            \r
+            // refresh button enabled\r
+            refreshButtonEnabled = savedInstanceState.getBoolean(KEY_REFRESH_BUTTON_ENABLED);\r
+            \r
+\r
+        }\r
+\r
+        if (mAuthMessageVisibility== View.VISIBLE) {\r
+            showAuthMessage(mAuthMessageText);\r
+        }\r
+        else {\r
+            hideAuthMessage();\r
+        }\r
+        adaptViewAccordingToAuthenticationMethod();\r
+        showServerStatus();\r
+        showAuthStatus();\r
+        \r
+        if (mAction == ACTION_UPDATE_TOKEN) {\r
+            /// lock things that should not change\r
+            mHostUrlInput.setEnabled(false);\r
+            mHostUrlInput.setFocusable(false);\r
+            mUsernameInput.setEnabled(false);\r
+            mUsernameInput.setFocusable(false);\r
+            mOAuth2Check.setVisibility(View.GONE);\r
+        }\r
+        \r
+        //if (mServerIsChecked && !mServerIsValid && mRefreshButtonEnabled) showRefreshButton();\r
+        if (mServerIsChecked && !mServerIsValid && refreshButtonEnabled) showRefreshButton();\r
+        mOkButton.setEnabled(mServerIsValid); // state not automatically recovered in configuration changes\r
+\r
+        if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType) || \r
+                !AUTH_OPTIONAL.equals(getString(R.string.auth_method_oauth2))) {\r
+            mOAuth2Check.setVisibility(View.GONE);\r
+        }\r
+\r
+        mPasswordInput.setText("");     // clean password to avoid social hacking (disadvantage: password in removed if the device is turned aside)\r
+\r
+        /// bind view elements to listeners and other friends\r
+        mHostUrlInput.setOnFocusChangeListener(this);\r
+        mHostUrlInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);\r
+        mHostUrlInput.setOnEditorActionListener(this);\r
+        mHostUrlInput.addTextChangedListener(new TextWatcher() {\r
+\r
+            @Override\r
+            public void afterTextChanged(Editable s) {\r
+                if (!mHostBaseUrl.equals(normalizeUrl(mHostUrlInput.getText().toString()))) {\r
+                    mOkButton.setEnabled(false);\r
+                }\r
+            }\r
+\r
+            @Override\r
+            public void beforeTextChanged(CharSequence s, int start, int count, int after) {\r
+            }\r
+\r
+            @Override\r
+            public void onTextChanged(CharSequence s, int start, int before, int count) {\r
+                if (!mResumed) {\r
+                    mAuthStatusIcon = 0;\r
+                    mAuthStatusText = 0;\r
+                    showAuthStatus();                    \r
+                }\r
+                mResumed = false;\r
+            }\r
+        });\r
+        \r
+        mPasswordInput.setOnFocusChangeListener(this);\r
+        mPasswordInput.setImeOptions(EditorInfo.IME_ACTION_DONE);\r
+        mPasswordInput.setOnEditorActionListener(this);
+        mPasswordInput.setOnTouchListener(new RightDrawableOnTouchListener() {\r
+            @Override\r
+            public boolean onDrawableTouch(final MotionEvent event) {\r
+                if (event.getAction() == MotionEvent.ACTION_UP) {\r
+                    AuthenticatorActivity.this.onViewPasswordClick();\r
+                }\r
+                return true;\r
+            }\r
+        });\r
+        \r
+        findViewById(R.id.scroll).setOnTouchListener(new OnTouchListener() {\r
+            @Override\r
+            public boolean onTouch(View view, MotionEvent event) {\r
+                if (event.getAction() == MotionEvent.ACTION_DOWN) {\r
+                    if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType) &&\r
+                            mHostUrlInput.hasFocus()) {\r
+                        checkOcServer();\r
+                    }\r
+                }\r
+                return false;\r
+            }\r
+        });\r
+    }\r
+    \r
+   \r
+\r
+    private void initAuthorizationMethod() {\r
+        boolean oAuthRequired = false;\r
+        boolean samlWebSsoRequired = false;\r
+\r
+        mAuthTokenType = getIntent().getExtras().getString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE);\r
+        mAccount = getIntent().getExtras().getParcelable(EXTRA_ACCOUNT);\r
+        \r
+        // TODO could be a good moment to validate the received token type, if not null\r
+        \r
+        if (mAuthTokenType == null) {    \r
+            if (mAccount != null) {\r
+                /// same authentication method than the one used to create the account to update\r
+                oAuthRequired = (mAccountMgr.getUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null);\r
+                samlWebSsoRequired = (mAccountMgr.getUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO) != null);\r
+            \r
+            } else {\r
+                /// use the one set in setup.xml\r
+                oAuthRequired = AUTH_ON.equals(getString(R.string.auth_method_oauth2));\r
+                samlWebSsoRequired = AUTH_ON.equals(getString(R.string.auth_method_saml_web_sso));            \r
+            }\r
+            if (oAuthRequired) {\r
+                mAuthTokenType = MainApp.getAuthTokenTypeAccessToken();\r
+            } else if (samlWebSsoRequired) {\r
+                mAuthTokenType = MainApp.getAuthTokenTypeSamlSessionCookie();\r
+            } else {\r
+                mAuthTokenType = MainApp.getAuthTokenTypePass();\r
+            }\r
+        }\r
+    \r
+        if (mAccount != null) {\r
+            String userName = mAccount.name.substring(0, mAccount.name.lastIndexOf('@'));\r
+            mUsernameInput.setText(userName);\r
+        }\r
+        \r
+        mOAuth2Check.setChecked(MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType));\r
+        \r
+    }\r
+\r
+    /**\r
+     * Saves relevant state before {@link #onPause()}\r
+     * \r
+     * Do NOT save {@link #mNewCapturedUriFromOAuth2Redirection}; it keeps a temporal flag, intended to defer the \r
+     * processing of the redirection caught in {@link #onNewIntent(Intent)} until {@link #onResume()} \r
+     * \r
+     * See {@link #loadSavedInstanceState(Bundle)}\r
+     */\r
+    @Override\r
+    protected void onSaveInstanceState(Bundle outState) {\r
+        super.onSaveInstanceState(outState);\r
+\r
+        /// connection state and info\r
+        outState.putInt(KEY_AUTH_MESSAGE_VISIBILITY, mAuthMessage.getVisibility());\r
+        outState.putString(KEY_AUTH_MESSAGE_TEXT, mAuthMessage.getText().toString());\r
+        outState.putInt(KEY_SERVER_STATUS_TEXT, mServerStatusText);\r
+        outState.putInt(KEY_SERVER_STATUS_ICON, mServerStatusIcon);\r
+        outState.putBoolean(KEY_SERVER_VALID, mServerIsValid);\r
+        outState.putBoolean(KEY_SERVER_CHECKED, mServerIsChecked);\r
+        outState.putBoolean(KEY_SERVER_CHECK_IN_PROGRESS, (!mServerIsValid && mOcServerChkOperation != null));\r
+        outState.putBoolean(KEY_IS_SSL_CONN, mIsSslConn);\r
+        outState.putBoolean(KEY_PASSWORD_VISIBLE, isPasswordVisible());\r
+        outState.putInt(KEY_AUTH_STATUS_ICON, mAuthStatusIcon);\r
+        outState.putInt(KEY_AUTH_STATUS_TEXT, mAuthStatusText);\r
+\r
+        /// server data\r
+        if (mDiscoveredVersion != null) {\r
+            outState.putString(KEY_OC_VERSION, mDiscoveredVersion.toString());\r
+        }\r
+        outState.putString(KEY_HOST_URL_TEXT, mHostBaseUrl);\r
+\r
+        /// account data, if updating\r
+        if (mAccount != null) {\r
+            outState.putParcelable(KEY_ACCOUNT, mAccount);\r
+        }\r
+        outState.putString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE, mAuthTokenType);\r
+        \r
+        // refresh button enabled\r
+        outState.putBoolean(KEY_REFRESH_BUTTON_ENABLED, (mRefreshButton.getVisibility() == View.VISIBLE));\r
+        \r
+\r
+    }\r
+\r
+\r
+    /**\r
+     * The redirection triggered by the OAuth authentication server as response to the GET AUTHORIZATION request\r
+     * is caught here.\r
+     * \r
+     * To make this possible, this activity needs to be qualified with android:launchMode = "singleTask" in the\r
+     * AndroidManifest.xml file.\r
+     */\r
+    @Override\r
+    protected void onNewIntent (Intent intent) {\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
+        }\r
+    }\r
+\r
+\r
+    /**\r
+     * The redirection triggered by the OAuth authentication server as response to the GET AUTHORIZATION, and \r
+     * deferred in {@link #onNewIntent(Intent)}, is processed here.\r
+     */\r
+    @Override\r
+    protected void onResume() {\r
+        super.onResume();\r
+        if (mAction == ACTION_UPDATE_TOKEN && mJustCreated && getIntent().getBooleanExtra(EXTRA_ENFORCED_UPDATE, false)) {\r
+            if (MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType)) {\r
+                //Toast.makeText(this, R.string.auth_expired_oauth_token_toast, Toast.LENGTH_LONG).show();\r
+                showAuthMessage(getString(R.string.auth_expired_oauth_token_toast));\r
+            } else if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) {\r
+                //Toast.makeText(this, R.string.auth_expired_saml_sso_token_toast, Toast.LENGTH_LONG).show();\r
+                showAuthMessage(getString(R.string.auth_expired_saml_sso_token_toast));\r
+            } else {\r
+                //Toast.makeText(this, R.string.auth_expired_basic_auth_toast, Toast.LENGTH_LONG).show();\r
+                showAuthMessage(getString(R.string.auth_expired_basic_auth_toast));\r
+            }\r
+        }\r
+\r
+        if (mNewCapturedUriFromOAuth2Redirection != null) {\r
+            getOAuth2AccessTokenFromCapturedRedirection();            \r
+        }\r
+\r
+        mJustCreated = false;\r
+        \r
+    }\r
+\r
+\r
+    /**\r
+     * Parses the redirection with the response to the GET AUTHORIZATION request to the \r
+     * oAuth server and requests for the access token (GET ACCESS TOKEN)\r
+     */\r
+    private void getOAuth2AccessTokenFromCapturedRedirection() {\r
+        /// Parse data from OAuth redirection\r
+        String queryParameters = mNewCapturedUriFromOAuth2Redirection.getQuery();\r
+        mNewCapturedUriFromOAuth2Redirection = null;\r
+\r
+        /// Showing the dialog with instructions for the user.\r
+        showDialog(DIALOG_OAUTH2_LOGIN_PROGRESS);\r
+\r
+        /// GET ACCESS TOKEN to the oAuth server \r
+        RemoteOperation operation = new OAuth2GetAccessToken(   getString(R.string.oauth2_client_id), \r
+                getString(R.string.oauth2_redirect_uri),       \r
+                getString(R.string.oauth2_grant_type),\r
+                queryParameters);\r
+        //WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(getString(R.string.oauth2_url_endpoint_access)), getApplicationContext());\r
+        WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mOAuthTokenEndpointText.getText().toString().trim()), getApplicationContext(), true);\r
+        operation.execute(client, this, mHandler);\r
+    }\r
+\r
+\r
+\r
+    /**\r
+     * Handles the change of focus on the text inputs for the server URL and the password\r
+     */\r
+    public void onFocusChange(View view, boolean hasFocus) {\r
+        if (view.getId() == R.id.hostUrlInput) {   \r
+            if (!hasFocus) {\r
+                onUrlInputFocusLost((TextView) view);\r
+            }\r
+            else {\r
+                hideRefreshButton();\r
+            }\r
+\r
+        } else if (view.getId() == R.id.account_password) {\r
+            onPasswordFocusChanged((TextView) view, hasFocus);\r
+        }\r
+    }\r
+\r
+\r
+    /**\r
+     * Handles changes in focus on the text input for the server URL.\r
+     * \r
+     * IMPORTANT ENTRY POINT 2: When (!hasFocus), user wrote the server URL and changed to \r
+     * other field. The operation to check the existence of the server in the entered URL is\r
+     * started. \r
+     * \r
+     * When hasFocus:    user 'comes back' to write again the server URL.\r
+     * \r
+     * @param hostInput     TextView with the URL input field receiving the change of focus.\r
+     */\r
+    private void onUrlInputFocusLost(TextView hostInput) {\r
+        if (!mHostBaseUrl.equals(normalizeUrl(mHostUrlInput.getText().toString()))) {\r
+            checkOcServer();\r
+        } else {\r
+            mOkButton.setEnabled(mServerIsValid);\r
+            if (!mServerIsValid) {\r
+                showRefreshButton();\r
+            }\r
+        }\r
+    }\r
+\r
+\r
+    private void checkOcServer() {\r
+        String uri = trimUrlWebdav(mHostUrlInput.getText().toString().trim());\r
+        \r
+        if (!mHostUrlInputEnabled){\r
+            uri = getString(R.string.server_url);\r
+        }\r
+        \r
+        mServerIsValid = false;\r
+        mServerIsChecked = false;\r
+        mOkButton.setEnabled(false);\r
+        mDiscoveredVersion = null;\r
+        hideRefreshButton();\r
+        if (uri.length() != 0) {\r
+            mServerStatusText = R.string.auth_testing_connection;\r
+            mServerStatusIcon = R.drawable.progress_small;\r
+            showServerStatus();\r
+            mOcServerChkOperation = new  OwnCloudServerCheckOperation(uri, this);\r
+            WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(uri), this, true);\r
+            mOperationThread = mOcServerChkOperation.execute(client, this, mHandler);\r
+        } else {\r
+            mServerStatusText = 0;\r
+            mServerStatusIcon = 0;\r
+            showServerStatus();\r
+        }\r
+    }\r
+\r
+\r
+    /**\r
+     * Handles changes in focus on the text input for the password (basic authorization).\r
+     * \r
+     * When (hasFocus), the button to toggle password visibility is shown.\r
+     * \r
+     * When (!hasFocus), the button is made invisible and the password is hidden.\r
+     * \r
+     * @param passwordInput    TextView with the password input field receiving the change of focus.\r
+     * @param hasFocus          'True' if focus is received, 'false' if is lost\r
+     */\r
+    private void onPasswordFocusChanged(TextView passwordInput, boolean hasFocus) {\r
+        if (hasFocus) {\r
+            showViewPasswordButton();\r
+        } else {\r
+            hidePassword();\r
+            hidePasswordButton();\r
+        }\r
+    }\r
+\r
+\r
+    private void showViewPasswordButton() {\r
+        //int drawable = android.R.drawable.ic_menu_view;\r
+        int drawable = R.drawable.ic_view;\r
+        if (isPasswordVisible()) {\r
+            //drawable = android.R.drawable.ic_secure;\r
+            drawable = R.drawable.ic_hide;\r
+        }\r
+        mPasswordInput.setCompoundDrawablesWithIntrinsicBounds(0, 0, drawable, 0);\r
+    }\r
+\r
+    private boolean isPasswordVisible() {\r
+        return ((mPasswordInput.getInputType() & InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);\r
+    }\r
+    \r
+    private void hidePasswordButton() {\r
+        mPasswordInput.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);\r
+    }\r
+\r
+    private void showPassword() {\r
+        mPasswordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);\r
+        showViewPasswordButton();\r
+    }\r
+    \r
+    private void hidePassword() {\r
+        mPasswordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);\r
+        showViewPasswordButton();\r
+    }\r
+    \r
+    \r
+    /**\r
+     * Cancels the authenticator activity\r
+     * \r
+     * IMPORTANT ENTRY POINT 3: Never underestimate the importance of cancellation\r
+     * \r
+     * This method is bound in the layout/acceoun_setup.xml resource file.\r
+     * \r
+     * @param view      Cancel button\r
+     */\r
+    public void onCancelClick(View view) {\r
+        setResult(RESULT_CANCELED);     // TODO review how is this related to AccountAuthenticator (debugging)\r
+        finish();\r
+    }\r
+\r
+\r
+\r
+    /**\r
+     * Checks the credentials of the user in the root of the ownCloud server\r
+     * before creating a new local account.\r
+     * \r
+     * For basic authorization, a check of existence of the root folder is\r
+     * performed.\r
+     * \r
+     * For OAuth, starts the flow to get an access token; the credentials test \r
+     * is postponed until it is available.\r
+     * \r
+     * IMPORTANT ENTRY POINT 4\r
+     * \r
+     * @param view      OK button\r
+     */\r
+    public void onOkClick(View view) {\r
+        // this check should be unnecessary\r
+        if (mDiscoveredVersion == null || !mDiscoveredVersion.isVersionValid()  || mHostBaseUrl == null || mHostBaseUrl.length() == 0) {\r
+            mServerStatusIcon = R.drawable.common_error;\r
+            mServerStatusText = R.string.auth_wtf_reenter_URL;\r
+            showServerStatus();\r
+            mOkButton.setEnabled(false);\r
+            Log_OC.wtf(TAG,  "The user was allowed to click 'connect' to an unchecked server!!");\r
+            return;\r
+        }\r
+\r
+        if (MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType)) {\r
+            startOauthorization();\r
+        } else if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) { \r
+            startSamlBasedFederatedSingleSignOnAuthorization();\r
+        } else {\r
+            checkBasicAuthorization();\r
+        }\r
+    }\r
+\r
+\r
+    /**\r
+     * Tests the credentials entered by the user performing a check of existence on \r
+     * the root folder of the ownCloud server.\r
+     */\r
+    private void checkBasicAuthorization() {\r
+        /// get the path to the root folder through WebDAV from the version server\r
+        String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType);\r
+\r
+        /// get basic credentials entered by user\r
+        String username = mUsernameInput.getText().toString();\r
+        String password = mPasswordInput.getText().toString();\r
+\r
+        /// be gentle with the user\r
+        showDialog(DIALOG_LOGIN_PROGRESS);\r
+\r
+        /// test credentials accessing the root folder\r
+        mAuthCheckOperation = new  ExistenceCheckOperation("", this, false);\r
+        WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, true);\r
+        client.setBasicCredentials(username, password);\r
+        mOperationThread = mAuthCheckOperation.execute(client, this, mHandler);\r
+    }\r
+\r
+\r
+    /**\r
+     * Starts the OAuth 'grant type' flow to get an access token, with \r
+     * a GET AUTHORIZATION request to the BUILT-IN authorization server. \r
+     */\r
+    private void startOauthorization() {\r
+        // be gentle with the user\r
+        mAuthStatusIcon = R.drawable.progress_small;\r
+        mAuthStatusText = R.string.oauth_login_connection;\r
+        showAuthStatus();\r
+        \r
+\r
+        // GET AUTHORIZATION request\r
+        //Uri uri = Uri.parse(getString(R.string.oauth2_url_endpoint_auth));\r
+        Uri uri = Uri.parse(mOAuthAuthEndpointText.getText().toString().trim());\r
+        Uri.Builder uriBuilder = uri.buildUpon();\r
+        uriBuilder.appendQueryParameter(OAuth2Constants.KEY_RESPONSE_TYPE, getString(R.string.oauth2_response_type));\r
+        uriBuilder.appendQueryParameter(OAuth2Constants.KEY_REDIRECT_URI, getString(R.string.oauth2_redirect_uri));   \r
+        uriBuilder.appendQueryParameter(OAuth2Constants.KEY_CLIENT_ID, getString(R.string.oauth2_client_id));\r
+        uriBuilder.appendQueryParameter(OAuth2Constants.KEY_SCOPE, getString(R.string.oauth2_scope));\r
+        //uriBuilder.appendQueryParameter(OAuth2Constants.KEY_STATE, whateverwewant);\r
+        uri = uriBuilder.build();\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
+\r
+\r
+    /**\r
+     * Starts the Web Single Sign On flow to get access to the root folder\r
+     * in the server.\r
+     */\r
+    private void startSamlBasedFederatedSingleSignOnAuthorization() {\r
+        // be gentle with the user\r
+        mAuthStatusIcon = R.drawable.progress_small;\r
+        mAuthStatusText = R.string.auth_connecting_auth_server;\r
+        showAuthStatus();\r
+        showDialog(DIALOG_LOGIN_PROGRESS);\r
+        \r
+        /// get the path to the root folder through WebDAV from the version server\r
+        String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType);\r
+\r
+        /// test credentials accessing the root folder\r
+        mAuthCheckOperation = new  ExistenceCheckOperation("", this, false);\r
+        WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, false);\r
+        mOperationThread = mAuthCheckOperation.execute(client, this, mHandler);\r
+      \r
+    }\r
+\r
+    /**\r
+     * Callback method invoked when a RemoteOperation executed by this Activity finishes.\r
+     * \r
+     * Dispatches the operation flow to the right method.\r
+     */\r
+    @Override\r
+    public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {\r
+\r
+        if (operation instanceof OwnCloudServerCheckOperation) {\r
+            onOcServerCheckFinish((OwnCloudServerCheckOperation) operation, result);\r
+\r
+        } else if (operation instanceof OAuth2GetAccessToken) {\r
+            onGetOAuthAccessTokenFinish((OAuth2GetAccessToken)operation, result);\r
+\r
+        } else if (operation instanceof ExistenceCheckOperation)  {\r
+            if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) {\r
+                onSamlBasedFederatedSingleSignOnAuthorizationStart(operation, result);\r
+                \r
+            } else {\r
+                onAuthorizationCheckFinish((ExistenceCheckOperation)operation, result);\r
+            }\r
+        }\r
+    }\r
+    \r
+    \r
+    private void onSamlBasedFederatedSingleSignOnAuthorizationStart(RemoteOperation operation, RemoteOperationResult result) {\r
+        try {\r
+            dismissDialog(DIALOG_LOGIN_PROGRESS);\r
+        } catch (IllegalArgumentException e) {\r
+            // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens\r
+        }\r
+        
+        //if (result.isTemporalRedirection() && result.isIdPRedirection()) {\r
+        if (result.isIdPRedirection()) {
+            String url = result.getRedirectedLocation();\r
+            String targetUrl = mHostBaseUrl + AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType);\r
+            \r
+            // Show dialog\r
+            mSamlDialog = SamlWebViewDialog.newInstance(url, targetUrl);            \r
+            mSamlDialog.show(getSupportFragmentManager(), TAG_SAML_DIALOG);\r
+            \r
+            mAuthStatusIcon = 0;\r
+            mAuthStatusText = 0;\r
+            \r
+        } else {\r
+            mAuthStatusIcon = R.drawable.common_error;\r
+            mAuthStatusText = R.string.auth_unsupported_auth_method;\r
+            \r
+        }\r
+        showAuthStatus();\r
+    }\r
+\r
+\r
+    /**\r
+     * Processes the result of the server check performed when the user finishes the enter of the\r
+     * server URL.\r
+     * \r
+     * @param operation     Server check performed.\r
+     * @param result        Result of the check.\r
+     */\r
+    private void onOcServerCheckFinish(OwnCloudServerCheckOperation operation, RemoteOperationResult result) {\r
+        if (operation.equals(mOcServerChkOperation)) {\r
+            /// save result state\r
+            mServerIsChecked = true;\r
+            mServerIsValid = result.isSuccess();\r
+            mIsSslConn = (result.getCode() == ResultCode.OK_SSL);\r
+            mOcServerChkOperation = null;\r
+\r
+            /// update status icon and text\r
+            if (mServerIsValid) {\r
+                hideRefreshButton();\r
+            } else {\r
+                showRefreshButton();\r
+            }\r
+            updateServerStatusIconAndText(result);\r
+            showServerStatus();\r
+\r
+            /// very special case (TODO: move to a common place for all the remote operations)\r
+            if (result.getCode() == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) {\r
+                mLastSslUntrustedServerResult = result;\r
+                showDialog(DIALOG_SSL_VALIDATOR); \r
+            }\r
+\r
+            /// retrieve discovered version and normalize server URL\r
+            mDiscoveredVersion = operation.getDiscoveredVersion();\r
+            mHostBaseUrl = normalizeUrl(mHostUrlInput.getText().toString());\r
+\r
+            /// allow or not the user try to access the server\r
+            mOkButton.setEnabled(mServerIsValid);\r
+\r
+        }   // else nothing ; only the last check operation is considered; \r
+        // multiple can be triggered if the user amends a URL before a previous check can be triggered\r
+    }\r
+\r
+\r
+    private String normalizeUrl(String url) {\r
+        if (url != null && url.length() > 0) {\r
+            url = url.trim();\r
+            if (!url.toLowerCase().startsWith("http://") &&\r
+                    !url.toLowerCase().startsWith("https://")) {\r
+                if (mIsSslConn) {\r
+                    url = "https://" + url;\r
+                } else {\r
+                    url = "http://" + url;\r
+                }\r
+            }\r
+\r
+            // OC-208: Add suffix remote.php/webdav to normalize (OC-34)            \r
+            url = trimUrlWebdav(url);\r
+\r
+            if (url.endsWith("/")) {\r
+                url = url.substring(0, url.length() - 1);\r
+            }\r
+\r
+        }\r
+        return (url != null ? url : "");\r
+    }\r
+\r
+\r
+    private String trimUrlWebdav(String url){       \r
+        if(url.toLowerCase().endsWith(AccountUtils.WEBDAV_PATH_4_0)){\r
+            url = url.substring(0, url.length() - AccountUtils.WEBDAV_PATH_4_0.length());             \r
+        } else if(url.toLowerCase().endsWith(AccountUtils.WEBDAV_PATH_2_0)){\r
+            url = url.substring(0, url.length() - AccountUtils.WEBDAV_PATH_2_0.length());             \r
+        } else if (url.toLowerCase().endsWith(AccountUtils.WEBDAV_PATH_1_2)){\r
+            url = url.substring(0, url.length() - AccountUtils.WEBDAV_PATH_1_2.length());             \r
+        } \r
+        return (url != null ? url : "");\r
+    }\r
+    \r
+    \r
+    /**\r
+     * Chooses the right icon and text to show to the user for the received operation result.\r
+     * \r
+     * @param result    Result of a remote operation performed in this activity\r
+     */\r
+    private void updateServerStatusIconAndText(RemoteOperationResult result) {\r
+        mServerStatusIcon = R.drawable.common_error;    // the most common case in the switch below\r
+\r
+        switch (result.getCode()) {\r
+        case OK_SSL:\r
+            mServerStatusIcon = android.R.drawable.ic_secure;\r
+            mServerStatusText = R.string.auth_secure_connection;\r
+            break;\r
+\r
+        case OK_NO_SSL:\r
+        case OK:\r
+            if (mHostUrlInput.getText().toString().trim().toLowerCase().startsWith("http://") ) {\r
+                mServerStatusText = R.string.auth_connection_established;\r
+                mServerStatusIcon = R.drawable.ic_ok;\r
+            } else {\r
+                mServerStatusText = R.string.auth_nossl_plain_ok_title;\r
+                mServerStatusIcon = android.R.drawable.ic_partial_secure;\r
+            }\r
+            break;\r
+\r
+        case NO_NETWORK_CONNECTION:\r
+            mServerStatusIcon = R.drawable.no_network;\r
+            mServerStatusText = R.string.auth_no_net_conn_title;\r
+            break;\r
+\r
+        case SSL_RECOVERABLE_PEER_UNVERIFIED:\r
+            mServerStatusText = R.string.auth_ssl_unverified_server_title;\r
+            break;\r
+        case BAD_OC_VERSION:\r
+            mServerStatusText = R.string.auth_bad_oc_version_title;\r
+            break;\r
+        case WRONG_CONNECTION:\r
+            mServerStatusText = R.string.auth_wrong_connection_title;\r
+            break;\r
+        case TIMEOUT:\r
+            mServerStatusText = R.string.auth_timeout_title;\r
+            break;\r
+        case INCORRECT_ADDRESS:\r
+            mServerStatusText = R.string.auth_incorrect_address_title;\r
+            break;\r
+        case SSL_ERROR:\r
+            mServerStatusText = R.string.auth_ssl_general_error_title;\r
+            break;\r
+        case UNAUTHORIZED:\r
+            mServerStatusText = R.string.auth_unauthorized;\r
+            break;\r
+        case HOST_NOT_AVAILABLE:\r
+            mServerStatusText = R.string.auth_unknown_host_title;\r
+            break;\r
+        case INSTANCE_NOT_CONFIGURED:\r
+            mServerStatusText = R.string.auth_not_configured_title;\r
+            break;\r
+        case FILE_NOT_FOUND:\r
+            mServerStatusText = R.string.auth_incorrect_path_title;\r
+            break;\r
+        case OAUTH2_ERROR:\r
+            mServerStatusText = R.string.auth_oauth_error;\r
+            break;\r
+        case OAUTH2_ERROR_ACCESS_DENIED:\r
+            mServerStatusText = R.string.auth_oauth_error_access_denied;\r
+            break;\r
+        case UNHANDLED_HTTP_CODE:\r
+        case UNKNOWN_ERROR:\r
+            mServerStatusText = R.string.auth_unknown_error_title;\r
+            break;\r
+        default:\r
+            mServerStatusText = 0;\r
+            mServerStatusIcon = 0;\r
+        }\r
+    }\r
+\r
+\r
+    /**\r
+     * Chooses the right icon and text to show to the user for the received operation result.\r
+     * \r
+     * @param result    Result of a remote operation performed in this activity\r
+     */\r
+    private void updateAuthStatusIconAndText(RemoteOperationResult result) {\r
+        mAuthStatusIcon = R.drawable.common_error;    // the most common case in the switch below\r
+\r
+        switch (result.getCode()) {\r
+        case OK_SSL:\r
+            mAuthStatusIcon = android.R.drawable.ic_secure;\r
+            mAuthStatusText = R.string.auth_secure_connection;\r
+            break;\r
+\r
+        case OK_NO_SSL:\r
+        case OK:\r
+            if (mHostUrlInput.getText().toString().trim().toLowerCase().startsWith("http://") ) {\r
+                mAuthStatusText = R.string.auth_connection_established;\r
+                mAuthStatusIcon = R.drawable.ic_ok;\r
+            } else {\r
+                mAuthStatusText = R.string.auth_nossl_plain_ok_title;\r
+                mAuthStatusIcon = android.R.drawable.ic_partial_secure;\r
+            }\r
+            break;\r
+\r
+        case NO_NETWORK_CONNECTION:\r
+            mAuthStatusIcon = R.drawable.no_network;\r
+            mAuthStatusText = R.string.auth_no_net_conn_title;\r
+            break;\r
+\r
+        case SSL_RECOVERABLE_PEER_UNVERIFIED:\r
+            mAuthStatusText = R.string.auth_ssl_unverified_server_title;\r
+            break;\r
+        case BAD_OC_VERSION:\r
+            mAuthStatusText = R.string.auth_bad_oc_version_title;\r
+            break;\r
+        case WRONG_CONNECTION:\r
+            mAuthStatusText = R.string.auth_wrong_connection_title;\r
+            break;\r
+        case TIMEOUT:\r
+            mAuthStatusText = R.string.auth_timeout_title;\r
+            break;\r
+        case INCORRECT_ADDRESS:\r
+            mAuthStatusText = R.string.auth_incorrect_address_title;\r
+            break;\r
+        case SSL_ERROR:\r
+            mAuthStatusText = R.string.auth_ssl_general_error_title;\r
+            break;\r
+        case UNAUTHORIZED:\r
+            mAuthStatusText = R.string.auth_unauthorized;\r
+            break;\r
+        case HOST_NOT_AVAILABLE:\r
+            mAuthStatusText = R.string.auth_unknown_host_title;\r
+            break;\r
+        case INSTANCE_NOT_CONFIGURED:\r
+            mAuthStatusText = R.string.auth_not_configured_title;\r
+            break;\r
+        case FILE_NOT_FOUND:\r
+            mAuthStatusText = R.string.auth_incorrect_path_title;\r
+            break;\r
+        case OAUTH2_ERROR:\r
+            mAuthStatusText = R.string.auth_oauth_error;\r
+            break;\r
+        case OAUTH2_ERROR_ACCESS_DENIED:\r
+            mAuthStatusText = R.string.auth_oauth_error_access_denied;\r
+            break;\r
+        case ACCOUNT_NOT_NEW:\r
+            mAuthStatusText = R.string.auth_account_not_new;\r
+            break;\r
+        case ACCOUNT_NOT_THE_SAME:\r
+            mAuthStatusText = R.string.auth_account_not_the_same;\r
+            break;\r
+        case UNHANDLED_HTTP_CODE:\r
+        case UNKNOWN_ERROR:\r
+            mAuthStatusText = R.string.auth_unknown_error_title;\r
+            break;\r
+        default:\r
+            mAuthStatusText = 0;\r
+            mAuthStatusIcon = 0;\r
+        }\r
+    }\r
+\r
+\r
+    /**\r
+     * Processes the result of the request for and access token send \r
+     * to an OAuth authorization server.\r
+     * \r
+     * @param operation     Operation performed requesting the access token.\r
+     * @param result        Result of the operation.\r
+     */\r
+    private void onGetOAuthAccessTokenFinish(OAuth2GetAccessToken operation, RemoteOperationResult result) {\r
+        try {\r
+            dismissDialog(DIALOG_OAUTH2_LOGIN_PROGRESS);\r
+        } catch (IllegalArgumentException e) {\r
+            // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens\r
+        }\r
+\r
+        String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType);\r
+        if (result.isSuccess() && webdav_path != null) {\r
+            /// be gentle with the user\r
+            showDialog(DIALOG_LOGIN_PROGRESS);\r
+\r
+            /// time to test the retrieved access token on the ownCloud server\r
+            mAuthToken = ((OAuth2GetAccessToken)operation).getResultTokenMap().get(OAuth2Constants.KEY_ACCESS_TOKEN);\r
+            Log_OC.d(TAG, "Got ACCESS TOKEN: " + mAuthToken);\r
+            mAuthCheckOperation = new ExistenceCheckOperation("", this, false);\r
+            WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, true);\r
+            client.setBearerCredentials(mAuthToken);\r
+            mAuthCheckOperation.execute(client, this, mHandler);\r
+\r
+        } else {\r
+            updateAuthStatusIconAndText(result);\r
+            showAuthStatus();\r
+            Log_OC.d(TAG, "Access failed: " + result.getLogMessage());\r
+        }\r
+    }\r
+\r
+\r
+    /**\r
+     * Processes the result of the access check performed to try the user credentials.\r
+     * \r
+     * Creates a new account through the AccountManager.\r
+     * \r
+     * @param operation     Access check performed.\r
+     * @param result        Result of the operation.\r
+     */\r
+    private void onAuthorizationCheckFinish(ExistenceCheckOperation operation, RemoteOperationResult result) {\r
+        try {\r
+            dismissDialog(DIALOG_LOGIN_PROGRESS);\r
+        } catch (IllegalArgumentException e) {\r
+            // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens\r
+        }\r
+\r
+        if (result.isSuccess()) {\r
+            Log_OC.d(TAG, "Successful access - time to save the account");\r
+\r
+            boolean success = false;\r
+            if (mAction == ACTION_CREATE) {\r
+                success = createAccount();\r
+\r
+            } else {\r
+                success = updateToken();\r
+            }\r
+\r
+            if (success) {\r
+                finish();\r
+            }\r
+\r
+        } else if (result.isServerFail() || result.isException()) {\r
+            /// if server fail or exception in authorization, the UI is updated as when a server check failed\r
+            mServerIsChecked = true;\r
+            mServerIsValid = false;\r
+            mIsSslConn = false;\r
+            mOcServerChkOperation = null;\r
+            mDiscoveredVersion = null;\r
+            mHostBaseUrl = normalizeUrl(mHostUrlInput.getText().toString());\r
+\r
+            // update status icon and text\r
+            updateServerStatusIconAndText(result);\r
+            showServerStatus();\r
+            mAuthStatusIcon = 0;\r
+            mAuthStatusText = 0;\r
+            showAuthStatus();\r
+            \r
+            // update input controls state\r
+            showRefreshButton();\r
+            mOkButton.setEnabled(false);\r
+\r
+            // very special case (TODO: move to a common place for all the remote operations) (dangerous here?)\r
+            if (result.getCode() == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) {\r
+                mLastSslUntrustedServerResult = result;\r
+                showDialog(DIALOG_SSL_VALIDATOR); \r
+            }\r
+\r
+        } else {    // authorization fail due to client side - probably wrong credentials\r
+            updateAuthStatusIconAndText(result);\r
+            showAuthStatus();\r
+            Log_OC.d(TAG, "Access failed: " + result.getLogMessage());\r
+        }\r
+\r
+    }\r
+\r
+\r
+    /**\r
+     * Sets the proper response to get that the Account Authenticator that started this activity saves \r
+     * a new authorization token for mAccount.\r
+     */\r
+    private boolean updateToken() {\r
+        Bundle response = new Bundle();\r
+        response.putString(AccountManager.KEY_ACCOUNT_NAME, mAccount.name);\r
+        response.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccount.type);\r
+        \r
+        if (MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType)) { \r
+            response.putString(AccountManager.KEY_AUTHTOKEN, mAuthToken);\r
+            // the next line is necessary; by now, notifications are calling directly to the AuthenticatorActivity to update, without AccountManager intervention\r
+            mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken);\r
+            \r
+        } else if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) {\r
+            String username = getUserNameForSamlSso();\r
+            if (!mUsernameInput.getText().toString().equals(username)) {\r
+                // fail - not a new account, but an existing one; disallow\r
+                RemoteOperationResult result = new RemoteOperationResult(ResultCode.ACCOUNT_NOT_THE_SAME); \r
+                updateAuthStatusIconAndText(result);\r
+                showAuthStatus();\r
+                Log_OC.d(TAG, result.getLogMessage());\r
+                \r
+                return false;\r
+            }\r
+            \r
+            response.putString(AccountManager.KEY_AUTHTOKEN, mAuthToken);\r
+            // the next line is necessary; by now, notifications are calling directly to the AuthenticatorActivity to update, without AccountManager intervention\r
+            mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken);\r
+            \r
+        } else {\r
+            response.putString(AccountManager.KEY_AUTHTOKEN, mPasswordInput.getText().toString());\r
+            mAccountMgr.setPassword(mAccount, mPasswordInput.getText().toString());\r
+        }\r
+        setAccountAuthenticatorResult(response);\r
+        \r
+        return true;\r
+    }\r
+\r
+\r
+    /**\r
+     * Creates a new account through the Account Authenticator that started this activity. \r
+     * \r
+     * This makes the account permanent.\r
+     * \r
+     * TODO Decide how to name the OAuth accounts\r
+     */\r
+    private boolean createAccount() {\r
+        /// create and save new ownCloud account\r
+        boolean isOAuth = MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType);\r
+        boolean isSaml =  MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType);\r
+\r
+        Uri uri = Uri.parse(mHostBaseUrl);\r
+        String username = mUsernameInput.getText().toString().trim();\r
+        if (isSaml) {\r
+            username = getUserNameForSamlSso();\r
+            \r
+        } else if (isOAuth) {\r
+            username = "OAuth_user" + (new java.util.Random(System.currentTimeMillis())).nextLong();\r
+        }            \r
+        String accountName = username + "@" + uri.getHost();\r
+        if (uri.getPort() >= 0) {\r
+            accountName += ":" + uri.getPort();\r
+        }\r
+        mAccount = new Account(accountName, MainApp.getAccountType());\r
+        if (AccountUtils.exists(mAccount, getApplicationContext())) {\r
+            // fail - not a new account, but an existing one; disallow\r
+            RemoteOperationResult result = new RemoteOperationResult(ResultCode.ACCOUNT_NOT_NEW); \r
+            updateAuthStatusIconAndText(result);\r
+            showAuthStatus();\r
+            Log_OC.d(TAG, result.getLogMessage());\r
+            return false;\r
+            \r
+        } else {\r
+        \r
+            if (isOAuth || isSaml) {\r
+                mAccountMgr.addAccountExplicitly(mAccount, "", null);  // with external authorizations, the password is never input in the app\r
+            } else {\r
+                mAccountMgr.addAccountExplicitly(mAccount, mPasswordInput.getText().toString(), null);\r
+            }\r
+    \r
+            /// add the new account as default in preferences, if there is none already\r
+            Account defaultAccount = AccountUtils.getCurrentOwnCloudAccount(this);\r
+            if (defaultAccount == null) {\r
+                SharedPreferences.Editor editor = PreferenceManager\r
+                        .getDefaultSharedPreferences(this).edit();\r
+                editor.putString("select_oc_account", accountName);\r
+                editor.commit();\r
+            }\r
+    \r
+            /// prepare result to return to the Authenticator\r
+            //  TODO check again what the Authenticator makes with it; probably has the same effect as addAccountExplicitly, but it's not well done\r
+            final Intent intent = new Intent();       \r
+            intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE,    MainApp.getAccountType());\r
+            intent.putExtra(AccountManager.KEY_ACCOUNT_NAME,    mAccount.name);\r
+            /*if (!isOAuth)\r
+                intent.putExtra(AccountManager.KEY_AUTHTOKEN,   MainApp.getAccountType()); */\r
+            intent.putExtra(AccountManager.KEY_USERDATA,        username);\r
+            if (isOAuth || isSaml) {\r
+                mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken);\r
+            }\r
+            /// add user data to the new account; TODO probably can be done in the last parameter addAccountExplicitly, or in KEY_USERDATA\r
+            mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION,    mDiscoveredVersion.toString());\r
+            mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_OC_BASE_URL,   mHostBaseUrl);\r
+            if (isSaml) {\r
+                mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO, "TRUE"); \r
+            } else if (isOAuth) {\r
+                mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_OAUTH2, "TRUE");  \r
+            }\r
+    \r
+            setAccountAuthenticatorResult(intent.getExtras());\r
+            setResult(RESULT_OK, intent);\r
+    \r
+            /// immediately request for the synchronization of the new account\r
+            Bundle bundle = new Bundle();\r
+            bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);\r
+            ContentResolver.requestSync(mAccount, MainApp.getAuthTokenType(), bundle);\r
+            syncAccount();\r
+//          Bundle bundle = new Bundle();\r
+//          bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);\r
+//          ContentResolver.requestSync(mAccount, MainApp.getAuthTokenType(), bundle);\r
+            return true;\r
+        }\r
+    }\r
+\r
+    \r
+    private String getUserNameForSamlSso() {\r
+        if (mAuthToken != null) {\r
+            String [] cookies = mAuthToken.split(";");\r
+            for (int i=0; i<cookies.length; i++) {\r
+                if (cookies[i].startsWith(KEY_OC_USERNAME_EQUALS )) {\r
+                    String value = Uri.decode(cookies[i].substring(KEY_OC_USERNAME_EQUALS.length()));\r
+                    return value;\r
+                }\r
+            }\r
+        }\r
+        return "";\r
+    }\r
+\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * \r
+     * Necessary to update the contents of the SSL Dialog\r
+     * \r
+     * TODO move to some common place for all possible untrusted SSL failures\r
+     */\r
+    @Override\r
+    protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {\r
+        switch (id) {\r
+        case DIALOG_LOGIN_PROGRESS:\r
+        case DIALOG_CERT_NOT_SAVED:\r
+        case DIALOG_OAUTH2_LOGIN_PROGRESS:\r
+            break;\r
+        case DIALOG_SSL_VALIDATOR: {\r
+            ((SslValidatorDialog)dialog).updateResult(mLastSslUntrustedServerResult);\r
+            break;\r
+        }\r
+        default:\r
+            Log_OC.e(TAG, "Incorrect dialog called with id = " + id);\r
+        }\r
+    }\r
+\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     */\r
+    @Override\r
+    protected Dialog onCreateDialog(int id) {\r
+        Dialog dialog = null;\r
+        switch (id) {\r
+        case DIALOG_LOGIN_PROGRESS: {\r
+            /// simple progress dialog\r
+            ProgressDialog working_dialog = new ProgressDialog(this);\r
+            working_dialog.setMessage(getResources().getString(R.string.auth_trying_to_login));\r
+            working_dialog.setIndeterminate(true);\r
+            working_dialog.setCancelable(true);\r
+            working_dialog\r
+            .setOnCancelListener(new DialogInterface.OnCancelListener() {\r
+                @Override\r
+                public void onCancel(DialogInterface dialog) {\r
+                    /// TODO study if this is enough\r
+                    Log_OC.i(TAG, "Login canceled");\r
+                    if (mOperationThread != null) {\r
+                        mOperationThread.interrupt();\r
+                        finish();\r
+                    }\r
+                }\r
+            });\r
+            dialog = working_dialog;\r
+            break;\r
+        }\r
+        case DIALOG_OAUTH2_LOGIN_PROGRESS: {\r
+            ProgressDialog working_dialog = new ProgressDialog(this);\r
+            working_dialog.setMessage(String.format("Getting authorization")); \r
+            working_dialog.setIndeterminate(true);\r
+            working_dialog.setCancelable(true);\r
+            working_dialog\r
+            .setOnCancelListener(new DialogInterface.OnCancelListener() {\r
+                @Override\r
+                public void onCancel(DialogInterface dialog) {\r
+                    Log_OC.i(TAG, "Login canceled");\r
+                    finish();\r
+                }\r
+            });\r
+            dialog = working_dialog;\r
+            break;\r
+        }\r
+        case DIALOG_SSL_VALIDATOR: {\r
+            /// TODO start to use new dialog interface, at least for this (it is a FragmentDialog already)\r
+            dialog = SslValidatorDialog.newInstance(this, mLastSslUntrustedServerResult, this);\r
+            break;\r
+        }\r
+        case DIALOG_CERT_NOT_SAVED: {\r
+            AlertDialog.Builder 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
+            Log_OC.e(TAG, "Incorrect dialog called with id = " + id);\r
+        }\r
+        return dialog;\r
+    }\r
+\r
+\r
+    /**\r
+     * Starts and activity to open the 'new account' page in the ownCloud web site\r
+     * \r
+     * @param view      'Account register' button\r
+     */\r
+    public void onRegisterClick(View view) {\r
+        Intent register = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_account_register)));\r
+        setResult(RESULT_CANCELED);\r
+        startActivity(register);\r
+    }\r
+\r
+\r
+    /**\r
+     * Updates the content and visibility state of the icon and text associated\r
+     * to the last check on the ownCloud server.\r
+     */\r
+    private void showServerStatus() {\r
+        TextView tv = (TextView) findViewById(R.id.server_status_text);\r
+\r
+        if (mServerStatusIcon == 0 && mServerStatusText == 0) {\r
+            tv.setVisibility(View.INVISIBLE);\r
+\r
+        } else {\r
+            tv.setText(mServerStatusText);\r
+            tv.setCompoundDrawablesWithIntrinsicBounds(mServerStatusIcon, 0, 0, 0);\r
+            tv.setVisibility(View.VISIBLE);\r
+        }\r
+\r
+    }\r
+\r
+\r
+    /**\r
+     * Updates the content and visibility state of the icon and text associated\r
+     * to the interactions with the OAuth authorization server.\r
+     */\r
+    private void showAuthStatus() {\r
+        if (mAuthStatusIcon == 0 && mAuthStatusText == 0) {\r
+            mAuthStatusLayout.setVisibility(View.INVISIBLE);\r
+\r
+        } else {\r
+            mAuthStatusLayout.setText(mAuthStatusText);\r
+            mAuthStatusLayout.setCompoundDrawablesWithIntrinsicBounds(mAuthStatusIcon, 0, 0, 0);\r
+            mAuthStatusLayout.setVisibility(View.VISIBLE);\r
+        }\r
+    }     \r
+\r
+\r
+    private void showRefreshButton() {\r
+        mRefreshButton.setVisibility(View.VISIBLE);\r
+    }\r
+\r
+    private void hideRefreshButton() {\r
+        mRefreshButton.setVisibility(View.GONE);\r
+    }\r
+\r
+    /**\r
+     * Called when the refresh button in the input field for ownCloud host is clicked.\r
+     * \r
+     * Performs a new check on the URL in the input field.\r
+     * \r
+     * @param view      Refresh 'button'\r
+     */\r
+    public void onRefreshClick(View view) {\r
+        checkOcServer();\r
+    }\r
+    \r
+    \r
+    /**\r
+     * Called when the eye icon in the password field is clicked.\r
+     * \r
+     * Toggles the visibility of the password in the field. \r
+     */\r
+    public void onViewPasswordClick() {\r
+        int selectionStart = mPasswordInput.getSelectionStart();\r
+        int selectionEnd = mPasswordInput.getSelectionEnd();\r
+        if (isPasswordVisible()) {\r
+            hidePassword();\r
+        } else {\r
+            showPassword();\r
+        }\r
+        mPasswordInput.setSelection(selectionStart, selectionEnd);\r
+    }    \r
+\r
+\r
+    /**\r
+     * Called when the checkbox for OAuth authorization is clicked.\r
+     * \r
+     * Hides or shows the input fields for user & password. \r
+     * \r
+     * @param view      'View password' 'button'\r
+     */\r
+    public void onCheckClick(View view) {\r
+        CheckBox oAuth2Check = (CheckBox)view;\r
+        if (oAuth2Check.isChecked()) {\r
+            mAuthTokenType = MainApp.getAuthTokenTypeAccessToken();\r
+        } else {\r
+            mAuthTokenType = MainApp.getAuthTokenTypePass();\r
+        }\r
+        adaptViewAccordingToAuthenticationMethod();\r
+    }\r
+\r
+    \r
+    /**\r
+     * Changes the visibility of input elements depending on\r
+     * the current authorization method.\r
+     */\r
+    private void adaptViewAccordingToAuthenticationMethod () {\r
+        if (MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType)) {\r
+            // OAuth 2 authorization\r
+            mOAuthAuthEndpointText.setVisibility(View.VISIBLE);\r
+            mOAuthTokenEndpointText.setVisibility(View.VISIBLE);\r
+            mUsernameInput.setVisibility(View.GONE);\r
+            mPasswordInput.setVisibility(View.GONE);\r
+            \r
+        } else if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) {\r
+            // SAML-based web Single Sign On\r
+            mOAuthAuthEndpointText.setVisibility(View.GONE);\r
+            mOAuthTokenEndpointText.setVisibility(View.GONE);\r
+            mUsernameInput.setVisibility(View.GONE);\r
+            mPasswordInput.setVisibility(View.GONE);\r
+        } else {\r
+            // basic HTTP authorization\r
+            mOAuthAuthEndpointText.setVisibility(View.GONE);\r
+            mOAuthTokenEndpointText.setVisibility(View.GONE);\r
+            mUsernameInput.setVisibility(View.VISIBLE);\r
+            mPasswordInput.setVisibility(View.VISIBLE);\r
+        }\r
+    }\r
+    \r
+    /**\r
+     * Called from SslValidatorDialog when a new server certificate was correctly saved.\r
+     */\r
+    public void onSavedCertificate() {\r
+        checkOcServer();\r
+    }\r
+\r
+    /**\r
+     * Called from SslValidatorDialog when a new server certificate could not be saved \r
+     * when the user requested it.\r
+     */\r
+    @Override\r
+    public void onFailedSavingCertificate() {\r
+        showDialog(DIALOG_CERT_NOT_SAVED);\r
+    }\r
+\r
+\r
+    /**\r
+     *  Called when the 'action' button in an IME is pressed ('enter' in software keyboard).\r
+     * \r
+     *  Used to trigger the authentication check when the user presses 'enter' after writing the password, \r
+     *  or to throw the server test when the only field on screen is the URL input field.\r
+     */\r
+    @Override\r
+    public boolean onEditorAction(TextView inputField, int actionId, KeyEvent event) {\r
+        if (actionId == EditorInfo.IME_ACTION_DONE && inputField != null && inputField.equals(mPasswordInput)) {\r
+            if (mOkButton.isEnabled()) {\r
+                mOkButton.performClick();\r
+            }\r
+            \r
+        } else if (actionId == EditorInfo.IME_ACTION_NEXT && inputField != null && inputField.equals(mHostUrlInput)) {\r
+            if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) {\r
+                checkOcServer();\r
+            }\r
+        }\r
+        return false;   // always return false to grant that the software keyboard is hidden anyway\r
+    }\r
+\r
+\r
+    private abstract static class RightDrawableOnTouchListener implements OnTouchListener  {\r
+\r
+        private int fuzz = 75;\r
+        \r
+        /**\r
+         * {@inheritDoc}\r
+         */\r
+        @Override\r
+        public boolean onTouch(View view, MotionEvent event) {\r
+            Drawable rightDrawable = null;\r
+            if (view instanceof TextView) {\r
+                Drawable[] drawables = ((TextView)view).getCompoundDrawables();\r
+                if (drawables.length > 2) {\r
+                    rightDrawable = drawables[2];\r
+                }\r
+            }\r
+            if (rightDrawable != null) {\r
+                final int x = (int) event.getX();\r
+                final int y = (int) event.getY();\r
+                final Rect bounds = rightDrawable.getBounds();\r
+                if (x >= (view.getRight() - bounds.width() - fuzz) && x <= (view.getRight() - view.getPaddingRight() + fuzz)\r
+                    && y >= (view.getPaddingTop() - fuzz) && y <= (view.getHeight() - view.getPaddingBottom()) + fuzz) {\r
+                    \r
+                    return onDrawableTouch(event);\r
+                }\r
+            }\r
+            return false;\r
+        }\r
+\r
+        public abstract boolean onDrawableTouch(final MotionEvent event);\r
+    }\r
+\r
+\r
+    public void onSamlDialogSuccess(String sessionCookie){\r
+        mAuthToken = sessionCookie;\r
+        \r
+        if (sessionCookie != null && sessionCookie.length() > 0) {\r
+            mAuthToken = sessionCookie;\r
+            boolean success = false;\r
+            if (mAction == ACTION_CREATE) {\r
+                success = createAccount();\r
+        \r
+            } else {\r
+                success = updateToken();\r
+            }\r
+            if (success) {\r
+                finish();\r
+            }\r
+        }\r
+\r
+            \r
+    }\r
+\r
+\r
+    @Override\r
+    public void onSsoFinished(String sessionCookies) {\r
+        //Toast.makeText(this, "got cookies: " + sessionCookie, Toast.LENGTH_LONG).show();\r
+\r
+        if (sessionCookies != null && sessionCookies.length() > 0) {\r
+            Log_OC.d(TAG, "Successful SSO - time to save the account");\r
+            onSamlDialogSuccess(sessionCookies);\r
+            Fragment fd = getSupportFragmentManager().findFragmentByTag(TAG_SAML_DIALOG);\r
+            if (fd != null && fd instanceof SherlockDialogFragment) {\r
+                Dialog d = ((SherlockDialogFragment)fd).getDialog();\r
+                if (d != null && d.isShowing()) {\r
+                    d.dismiss();\r
+                }\r
+            }\r
+\r
+        } else { \r
+            // TODO - show fail\r
+            Log_OC.d(TAG, "SSO failed");\r
+        }\r
+    \r
+    }\r
+    \r
+    /** Show auth_message \r
+     * \r
+     * @param message\r
+     */\r
+    private void showAuthMessage(String message) {\r
+       mAuthMessage.setVisibility(View.VISIBLE);\r
+       mAuthMessage.setText(message);\r
+    }\r
+    \r
+    private void hideAuthMessage() {\r
+        mAuthMessage.setVisibility(View.GONE);\r
+    }\r
+\r
+    private void syncAccount(){\r
+        /// immediately request for the synchronization of the new account\r
+        Bundle bundle = new Bundle();\r
+        bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);\r
+        ContentResolver.requestSync(mAccount, MainApp.getAuthTokenType(), bundle);\r
+    }\r
+    \r
+    @Override\r
+    public boolean onTouchEvent(MotionEvent event) {\r
+        if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType) &&\r
+                mHostUrlInput.hasFocus() && event.getAction() == MotionEvent.ACTION_DOWN) {\r
+            checkOcServer();\r
+        }\r
+        return super.onTouchEvent(event);\r
+    }\r
+}\r
diff --git a/src/com/owncloud/android/authentication/OAuth2Constants.java b/src/com/owncloud/android/authentication/OAuth2Constants.java
new file mode 100644 (file)
index 0000000..f96b627
--- /dev/null
@@ -0,0 +1,53 @@
+/* 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.authentication;
+
+/** 
+ * Constant values for OAuth 2 protocol.
+ * 
+ * Includes required and optional parameter NAMES used in the 'authorization code' grant type.
+ *  
+ * @author David A. Velasco
+ */
+
+public class OAuth2Constants {
+    
+    /// Parameters to send to the Authorization Endpoint
+    public static final String KEY_RESPONSE_TYPE = "response_type";
+    public static final String KEY_REDIRECT_URI = "redirect_uri";
+    public static final String KEY_CLIENT_ID = "client_id";
+    public static final String KEY_SCOPE = "scope";
+    public static final String KEY_STATE = "state"; 
+    
+    /// Additional parameters to send to the Token Endpoint
+    public static final String KEY_GRANT_TYPE = "grant_type";
+    public static final String KEY_CODE = "code";
+    
+    /// Parameters received in an OK response from the Token Endpoint 
+    public static final String KEY_ACCESS_TOKEN = "access_token";
+    public static final String KEY_TOKEN_TYPE = "token_type";
+    public static final String KEY_EXPIRES_IN = "expires_in";
+    public static final String KEY_REFRESH_TOKEN = "refresh_token";
+    
+    /// Parameters in an ERROR response
+    public static final String KEY_ERROR = "error";
+    public static final String KEY_ERROR_DESCRIPTION = "error_description";
+    public static final String KEY_ERROR_URI = "error_uri";
+    public static final String VALUE_ERROR_ACCESS_DENIED = "access_denied";
+    
+}
diff --git a/src/com/owncloud/android/authentication/SsoWebViewClient.java b/src/com/owncloud/android/authentication/SsoWebViewClient.java
new file mode 100644 (file)
index 0000000..5c97931
--- /dev/null
@@ -0,0 +1,176 @@
+/* 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.authentication;
+
+import java.lang.ref.WeakReference;
+
+import com.owncloud.android.Log_OC;
+
+
+import android.graphics.Bitmap;
+import android.os.Handler;
+import android.os.Message;
+import android.view.View;
+import android.webkit.CookieManager;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+
+/**
+ * Custom {@link WebViewClient} client aimed to catch the end of a single-sign-on process 
+ * running in the {@link WebView} that is attached to.
+ * 
+ * Assumes that the single-sign-on is kept thanks to a cookie set at the end of the
+ * authentication process.
+ *   
+ * @author David A. Velasco
+ */
+public class SsoWebViewClient extends WebViewClient {
+        
+    private static final String TAG = SsoWebViewClient.class.getSimpleName();
+    
+    public interface SsoWebViewClientListener {
+        public void onSsoFinished(String sessionCookie);
+    }
+    
+    private Handler mListenerHandler;
+    private WeakReference<SsoWebViewClientListener> mListenerRef;
+    private String mTargetUrl;
+    private String mLastReloadedUrlAtError;
+    
+    public SsoWebViewClient (Handler listenerHandler, SsoWebViewClientListener listener) {
+        mListenerHandler = listenerHandler;
+        mListenerRef = new WeakReference<SsoWebViewClient.SsoWebViewClientListener>(listener);
+        mTargetUrl = "fake://url.to.be.set";
+        mLastReloadedUrlAtError = null;
+    }
+    
+    public String getTargetUrl() {
+        return mTargetUrl;
+    }
+    
+    public void setTargetUrl(String targetUrl) {
+        mTargetUrl = targetUrl;
+    }
+
+    @Override
+    public void onPageStarted (WebView view, String url, Bitmap favicon) {
+        Log_OC.d(TAG, "onPageStarted : " + url);
+        super.onPageStarted(view, url, favicon);
+    }
+    
+    @Override
+    public void onFormResubmission (WebView view, Message dontResend, Message resend) {
+        Log_OC.d(TAG, "onFormResubMission ");
+
+        // necessary to grant reload of last page when device orientation is changed after sending a form
+        resend.sendToTarget();
+    }
+
+    @Override
+    public boolean shouldOverrideUrlLoading(WebView view, String url) {
+        return false;
+    }
+    
+    @Override
+    public void onReceivedError (WebView view, int errorCode, String description, String failingUrl) {
+        Log_OC.e(TAG, "onReceivedError : " + failingUrl + ", code " + errorCode + ", description: " + description);
+        if (!failingUrl.equals(mLastReloadedUrlAtError)) {
+            view.reload();
+            mLastReloadedUrlAtError = failingUrl;
+        } else {
+            mLastReloadedUrlAtError = null;
+            super.onReceivedError(view, errorCode, description, failingUrl);
+        }
+    }
+    
+    @Override
+    public void onPageFinished (WebView view, String url) {
+        Log_OC.d(TAG, "onPageFinished : " + url);
+        mLastReloadedUrlAtError = null;
+        if (url.startsWith(mTargetUrl)) {
+            view.setVisibility(View.GONE);
+            CookieManager cookieManager = CookieManager.getInstance();
+            final String cookies = cookieManager.getCookie(url);
+            //Log_OC.d(TAG, "Cookies: " + cookies);
+            if (mListenerHandler != null && mListenerRef != null) {
+                // this is good idea because onPageFinished is not running in the UI thread
+                mListenerHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        SsoWebViewClientListener listener = mListenerRef.get();
+                        if (listener != null) {
+                            listener.onSsoFinished(cookies);
+                        }
+                    }
+                });
+            }
+        }
+
+    }
+    
+    /*
+    @Override
+    public void doUpdateVisitedHistory (WebView view, String url, boolean isReload) {
+        Log_OC.d(TAG, "doUpdateVisitedHistory : " + url);
+    }
+    
+    @Override
+    public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) {
+        Log_OC.d(TAG, "onReceivedSslError : " + error);
+    }
+    
+    @Override
+    public void onReceivedHttpAuthRequest (WebView view, HttpAuthHandler handler, String host, String realm) {
+        Log_OC.d(TAG, "onReceivedHttpAuthRequest : " + host);
+    }
+
+    @Override
+    public WebResourceResponse shouldInterceptRequest (WebView view, String url) {
+        Log_OC.d(TAG, "shouldInterceptRequest : " + url);
+        return null;
+    }
+    
+    @Override
+    public void onLoadResource (WebView view, String url) {
+        Log_OC.d(TAG, "onLoadResource : " + url);   
+    }
+    
+    @Override
+    public void onReceivedLoginRequest (WebView view, String realm, String account, String args) {
+        Log_OC.d(TAG, "onReceivedLoginRequest : " + realm + ", " + account + ", " + args);
+    }
+    
+    @Override
+    public void onScaleChanged (WebView view, float oldScale, float newScale) {
+        Log_OC.d(TAG, "onScaleChanged : " + oldScale + " -> " + newScale);
+        super.onScaleChanged(view, oldScale, newScale);
+    }
+
+    @Override
+    public void onUnhandledKeyEvent (WebView view, KeyEvent event) {
+        Log_OC.d(TAG, "onUnhandledKeyEvent : " + event);
+    }
+    
+    @Override
+    public boolean shouldOverrideKeyEvent (WebView view, KeyEvent event) {
+        Log_OC.d(TAG, "shouldOverrideKeyEvent : " + event);
+        return false;
+    }
+    */
+}
diff --git a/src/com/owncloud/android/datamodel/DataStorageManager.java b/src/com/owncloud/android/datamodel/DataStorageManager.java
new file mode 100644 (file)
index 0000000..133ab8d
--- /dev/null
@@ -0,0 +1,52 @@
+/* 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.datamodel;
+
+import java.util.List;
+import java.util.Vector;
+
+public interface DataStorageManager {
+
+    public static final int ROOT_PARENT_ID = 0;
+    
+    public OCFile getFileByPath(String path);
+
+    public OCFile getFileById(long id);
+
+    public boolean fileExists(String path);
+
+    public boolean fileExists(long id);
+
+    public boolean saveFile(OCFile file);
+
+    public void saveFiles(List<OCFile> files);
+
+    public Vector<OCFile> getDirectoryContent(OCFile f);
+    
+    public void removeFile(OCFile file, boolean removeLocalCopy);
+    
+    public void removeDirectory(OCFile dir, boolean removeDBData, boolean removeLocalContent);
+
+    public void moveDirectory(OCFile dir, String newPath);
+
+    public Vector<OCFile> getDirectoryImages(OCFile mParentFolder);
+    
+    public void calculateFolderSize(long id);
+    
+}
diff --git a/src/com/owncloud/android/datamodel/FileDataStorageManager.java b/src/com/owncloud/android/datamodel/FileDataStorageManager.java
new file mode 100644 (file)
index 0000000..9933e36
--- /dev/null
@@ -0,0 +1,690 @@
+/* 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.datamodel;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
+import com.owncloud.android.utils.FileStorageUtils;
+
+
+import android.accounts.Account;
+import android.content.ContentProviderClient;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.OperationApplicationException;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.RemoteException;
+
+public class FileDataStorageManager implements DataStorageManager {
+
+    private ContentResolver mContentResolver;
+    private ContentProviderClient mContentProvider;
+    private Account mAccount;
+
+    private static String TAG = "FileDataStorageManager";
+
+    public FileDataStorageManager(Account account, ContentResolver cr) {
+        mContentProvider = null;
+        mContentResolver = cr;
+        mAccount = account;
+    }
+
+    public FileDataStorageManager(Account account, ContentProviderClient cp) {
+        mContentProvider = cp;
+        mContentResolver = null;
+        mAccount = account;
+    }
+
+    @Override
+    public OCFile getFileByPath(String path) {
+        Cursor c = getCursorForValue(ProviderTableMeta.FILE_PATH, path);
+        OCFile file = null;
+        if (c.moveToFirst()) {
+            file = createFileInstance(c);
+        }
+        c.close();
+        if (file == null && OCFile.PATH_SEPARATOR.equals(path)) {
+            return createRootDir(); // root should always exist
+        }
+        return file;
+    }
+
+
+    private OCFile createRootDir() {
+        OCFile file = new OCFile(OCFile.PATH_SEPARATOR);
+        file.setMimetype("DIR");
+        file.setParentId(DataStorageManager.ROOT_PARENT_ID);
+        saveFile(file);
+        return file;
+    }
+
+    @Override
+    public OCFile getFileById(long id) {
+        Cursor c = getCursorForValue(ProviderTableMeta._ID, String.valueOf(id));
+        OCFile file = null;
+        if (c.moveToFirst()) {
+            file = createFileInstance(c);
+        }
+        c.close();
+        return file;
+    }
+
+    public OCFile getFileByLocalPath(String path) {
+        Cursor c = getCursorForValue(ProviderTableMeta.FILE_STORAGE_PATH, path);
+        OCFile file = null;
+        if (c.moveToFirst()) {
+            file = createFileInstance(c);
+        }
+        c.close();
+        return file;
+    }
+
+    @Override
+    public boolean fileExists(long id) {
+        return fileExists(ProviderTableMeta._ID, String.valueOf(id));
+    }
+
+    @Override
+    public boolean fileExists(String path) {
+        return fileExists(ProviderTableMeta.FILE_PATH, path);
+    }
+
+    @Override
+    public boolean saveFile(OCFile file) {
+        boolean overriden = false;
+        ContentValues cv = new ContentValues();
+        cv.put(ProviderTableMeta.FILE_MODIFIED, file.getModificationTimestamp());
+        cv.put(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, file.getModificationTimestampAtLastSyncForData());
+        cv.put(ProviderTableMeta.FILE_CREATION, file.getCreationTimestamp());
+        cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
+        cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
+        cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
+        if (file.getParentId() != 0)
+            cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
+        cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
+        if (!file.isDirectory())
+            cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
+        cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
+        cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
+        cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
+        cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.keepInSync() ? 1 : 0);
+
+        boolean sameRemotePath = fileExists(file.getRemotePath());
+        boolean changesSizeOfAncestors = false;
+        if (sameRemotePath ||
+                fileExists(file.getFileId())        ) {           // for renamed files; no more delete and create
+
+            OCFile oldFile = null;
+            if (sameRemotePath) {
+                oldFile = getFileByPath(file.getRemotePath());
+                file.setFileId(oldFile.getFileId());
+            } else {
+                oldFile = getFileById(file.getFileId());
+            }
+            changesSizeOfAncestors = (oldFile.getFileLength() != file.getFileLength());
+
+            overriden = true;
+            if (getContentResolver() != null) {
+                getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv,
+                        ProviderTableMeta._ID + "=?",
+                        new String[] { String.valueOf(file.getFileId()) });
+            } else {
+                try {
+                    getContentProvider().update(ProviderTableMeta.CONTENT_URI,
+                            cv, ProviderTableMeta._ID + "=?",
+                            new String[] { String.valueOf(file.getFileId()) });
+                } catch (RemoteException e) {
+                    Log_OC.e(TAG,
+                            "Fail to insert insert file to database "
+                                    + e.getMessage());
+                }
+            }
+        } else {
+            changesSizeOfAncestors = true;
+            Uri result_uri = null;
+            if (getContentResolver() != null) {
+                result_uri = getContentResolver().insert(
+                        ProviderTableMeta.CONTENT_URI_FILE, cv);
+            } else {
+                try {
+                    result_uri = getContentProvider().insert(
+                            ProviderTableMeta.CONTENT_URI_FILE, cv);
+                } catch (RemoteException e) {
+                    Log_OC.e(TAG,
+                            "Fail to insert insert file to database "
+                                    + e.getMessage());
+                }
+            }
+            if (result_uri != null) {
+                long new_id = Long.parseLong(result_uri.getPathSegments()
+                        .get(1));
+                file.setFileId(new_id);
+            }            
+        }
+
+        if (file.isDirectory()) {
+            calculateFolderSize(file.getFileId());
+            if (file.needsUpdatingWhileSaving()) {
+                for (OCFile f : getDirectoryContent(file))
+                    saveFile(f);
+            }
+        }
+        
+        if (changesSizeOfAncestors || file.isDirectory()) {
+            updateSizesToTheRoot(file.getParentId());
+        }
+        
+        return overriden;
+    }
+
+
+    @Override
+    public void saveFiles(List<OCFile> files) {
+
+        Iterator<OCFile> filesIt = files.iterator();
+        ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(files.size());
+        OCFile file = null;
+
+        // prepare operations to perform
+        while (filesIt.hasNext()) {
+            file = filesIt.next();
+            ContentValues cv = new ContentValues();
+            cv.put(ProviderTableMeta.FILE_MODIFIED, file.getModificationTimestamp());
+            cv.put(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, file.getModificationTimestampAtLastSyncForData());
+            cv.put(ProviderTableMeta.FILE_CREATION, file.getCreationTimestamp());
+            cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
+            cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
+            cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
+            if (file.getParentId() != 0)
+                cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
+            cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
+            if (!file.isDirectory())
+                cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
+            cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
+            cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
+            cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
+            cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.keepInSync() ? 1 : 0);
+
+            if (fileExists(file.getRemotePath())) {
+                OCFile oldFile = getFileByPath(file.getRemotePath());
+                file.setFileId(oldFile.getFileId());
+               
+                if (file.isDirectory()) {
+                    cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, oldFile.getFileLength());
+                    file.setFileLength(oldFile.getFileLength());
+                }
+                
+                operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
+                        withValues(cv).
+                        withSelection(  ProviderTableMeta._ID + "=?", 
+                                new String[] { String.valueOf(file.getFileId()) })
+                                .build());
+
+            } else if (fileExists(file.getFileId())) {
+                OCFile oldFile = getFileById(file.getFileId());
+                if (file.getStoragePath() == null && oldFile.getStoragePath() != null)
+                    file.setStoragePath(oldFile.getStoragePath());
+                
+                if (!file.isDirectory())
+                    cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
+                else {
+                    cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, oldFile.getFileLength());
+                    file.setFileLength(oldFile.getFileLength());
+                }
+                
+                operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
+                        withValues(cv).
+                        withSelection(  ProviderTableMeta._ID + "=?", 
+                                new String[] { String.valueOf(file.getFileId()) })
+                                .build());
+
+            } else {
+                operations.add(ContentProviderOperation.newInsert(ProviderTableMeta.CONTENT_URI).withValues(cv).build());
+            }
+        }
+
+        // apply operations in batch
+        ContentProviderResult[] results = null;
+        try {
+            if (getContentResolver() != null) {
+                results = getContentResolver().applyBatch(MainApp.getAuthority(), operations);
+
+            } else {
+                results = getContentProvider().applyBatch(operations);
+            }
+
+        } catch (OperationApplicationException e) {
+            Log_OC.e(TAG, "Fail to update/insert list of files to database " + e.getMessage());
+
+        } catch (RemoteException e) {
+            Log_OC.e(TAG, "Fail to update/insert list of files to database " + e.getMessage());
+        }
+
+        // update new id in file objects for insertions
+        if (results != null) {
+            long newId;
+            for (int i=0; i<results.length; i++) {
+                if (results[i].uri != null) {
+                    newId = Long.parseLong(results[i].uri.getPathSegments().get(1));
+                    files.get(i).setFileId(newId);
+                    //Log_OC.v(TAG, "Found and added id in insertion for " + files.get(i).getRemotePath());
+                }
+            }
+        }
+
+        for (OCFile aFile : files) {
+            if (aFile.isDirectory() && aFile.needsUpdatingWhileSaving())
+                saveFiles(getDirectoryContent(aFile));
+        }
+
+    }
+
+    public void setAccount(Account account) {
+        mAccount = account;
+    }
+
+    public Account getAccount() {
+        return mAccount;
+    }
+
+    public void setContentResolver(ContentResolver cr) {
+        mContentResolver = cr;
+    }
+
+    public ContentResolver getContentResolver() {
+        return mContentResolver;
+    }
+
+    public void setContentProvider(ContentProviderClient cp) {
+        mContentProvider = cp;
+    }
+
+    public ContentProviderClient getContentProvider() {
+        return mContentProvider;
+    }
+    
+    @Override
+    public Vector<OCFile> getDirectoryContent(OCFile f) {
+        if (f != null && f.isDirectory() && f.getFileId() != -1) {
+            return getDirectoryContent(f.getFileId());
+
+        } else {
+            return new Vector<OCFile>();
+        }
+    }
+
+    private Vector<OCFile> getDirectoryContent(long parentId) {
+
+        Vector<OCFile> ret = new Vector<OCFile>();
+
+        Uri req_uri = Uri.withAppendedPath(
+                ProviderTableMeta.CONTENT_URI_DIR,
+                String.valueOf(parentId));
+        Cursor c = null;
+
+        if (getContentProvider() != null) {
+            try {
+                c = getContentProvider().query(req_uri, null, 
+                        ProviderTableMeta.FILE_PARENT + "=?" ,
+                        new String[] { String.valueOf(parentId)}, null);
+            } catch (RemoteException e) {
+                Log_OC.e(TAG, e.getMessage());
+                return ret;
+            }
+        } else {
+            c = getContentResolver().query(req_uri, null, 
+                    ProviderTableMeta.FILE_PARENT + "=?" ,
+                    new String[] { String.valueOf(parentId)}, null);
+        }
+
+        if (c.moveToFirst()) {
+            do {
+                OCFile child = createFileInstance(c);
+                ret.add(child);
+            } while (c.moveToNext());
+        }
+
+        c.close();
+
+        Collections.sort(ret);
+
+        return ret;
+    }
+    
+    
+
+    private boolean fileExists(String cmp_key, String value) {
+        Cursor c;
+        if (getContentResolver() != null) {
+            c = getContentResolver()
+                    .query(ProviderTableMeta.CONTENT_URI,
+                            null,
+                            cmp_key + "=? AND "
+                                    + ProviderTableMeta.FILE_ACCOUNT_OWNER
+                                    + "=?",
+                                    new String[] { value, mAccount.name }, null);
+        } else {
+            try {
+                c = getContentProvider().query(
+                        ProviderTableMeta.CONTENT_URI,
+                        null,
+                        cmp_key + "=? AND "
+                                + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?",
+                                new String[] { value, mAccount.name }, null);
+            } catch (RemoteException e) {
+                Log_OC.e(TAG,
+                        "Couldn't determine file existance, assuming non existance: "
+                                + e.getMessage());
+                return false;
+            }
+        }
+        boolean retval = c.moveToFirst();
+        c.close();
+        return retval;
+    }
+
+    private Cursor getCursorForValue(String key, String value) {
+        Cursor c = null;
+        if (getContentResolver() != null) {
+            c = getContentResolver()
+                    .query(ProviderTableMeta.CONTENT_URI,
+                            null,
+                            key + "=? AND "
+                                    + ProviderTableMeta.FILE_ACCOUNT_OWNER
+                                    + "=?",
+                                    new String[] { value, mAccount.name }, null);
+        } else {
+            try {
+                c = getContentProvider().query(
+                        ProviderTableMeta.CONTENT_URI,
+                        null,
+                        key + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER
+                        + "=?", new String[] { value, mAccount.name },
+                        null);
+            } catch (RemoteException e) {
+                Log_OC.e(TAG, "Could not get file details: " + e.getMessage());
+                c = null;
+            }
+        }
+        return c;
+    }
+
+    private OCFile createFileInstance(Cursor c) {
+        OCFile file = null;
+        if (c != null) {
+            file = new OCFile(c.getString(c
+                    .getColumnIndex(ProviderTableMeta.FILE_PATH)));
+            file.setFileId(c.getLong(c.getColumnIndex(ProviderTableMeta._ID)));
+            file.setParentId(c.getLong(c
+                    .getColumnIndex(ProviderTableMeta.FILE_PARENT)));
+            file.setMimetype(c.getString(c
+                    .getColumnIndex(ProviderTableMeta.FILE_CONTENT_TYPE)));
+            if (!file.isDirectory()) {
+                file.setStoragePath(c.getString(c
+                        .getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH)));
+                if (file.getStoragePath() == null) {
+                    // try to find existing file and bind it with current account; - with the current update of SynchronizeFolderOperation, this won't be necessary anymore after a full synchronization of the account
+                    File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
+                    if (f.exists()) {
+                        file.setStoragePath(f.getAbsolutePath());
+                        file.setLastSyncDateForData(f.lastModified());
+                    }
+                }
+            }
+            file.setFileLength(c.getLong(c
+                    .getColumnIndex(ProviderTableMeta.FILE_CONTENT_LENGTH)));
+            file.setCreationTimestamp(c.getLong(c
+                    .getColumnIndex(ProviderTableMeta.FILE_CREATION)));
+            file.setModificationTimestamp(c.getLong(c
+                    .getColumnIndex(ProviderTableMeta.FILE_MODIFIED)));
+            file.setModificationTimestampAtLastSyncForData(c.getLong(c
+                    .getColumnIndex(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA)));
+            file.setLastSyncDateForProperties(c.getLong(c
+                    .getColumnIndex(ProviderTableMeta.FILE_LAST_SYNC_DATE)));
+            file.setLastSyncDateForData(c.getLong(c.
+                    getColumnIndex(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA)));
+            file.setKeepInSync(c.getInt(
+                    c.getColumnIndex(ProviderTableMeta.FILE_KEEP_IN_SYNC)) == 1 ? true : false);
+        }
+        return file;
+    }
+
+    @Override
+    public void removeFile(OCFile file, boolean removeLocalCopy) {
+        Uri file_uri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, ""+file.getFileId());
+        if (getContentProvider() != null) {
+            try {
+                getContentProvider().delete(file_uri,
+                        ProviderTableMeta.FILE_ACCOUNT_OWNER+"=?",
+                        new String[]{mAccount.name});
+            } catch (RemoteException e) {
+                e.printStackTrace();
+            }
+        } else {
+            getContentResolver().delete(file_uri,
+                    ProviderTableMeta.FILE_ACCOUNT_OWNER+"=?",
+                    new String[]{mAccount.name});
+        }
+        if (file.isDown() && removeLocalCopy) {
+            new File(file.getStoragePath()).delete();
+        }
+        if (file.isDirectory() && removeLocalCopy) {
+            File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
+            if (f.exists() && f.isDirectory() && (f.list() == null || f.list().length == 0)) {
+                f.delete();
+            }
+        }
+        
+        if (file.getFileLength() > 0) {
+            updateSizesToTheRoot(file.getParentId());
+        }
+    }
+
+    @Override
+    public void removeDirectory(OCFile dir, boolean removeDBData, boolean removeLocalContent) {
+        // TODO consider possible failures
+        if (dir != null && dir.isDirectory() && dir.getFileId() != -1) {
+            Vector<OCFile> children = getDirectoryContent(dir);
+            if (children.size() > 0) {
+                OCFile child = null;
+                for (int i=0; i<children.size(); i++) {
+                    child = children.get(i);
+                    if (child.isDirectory()) {
+                        removeDirectory(child, removeDBData, removeLocalContent);
+                    } else {
+                        if (removeDBData) {
+                            removeFile(child, removeLocalContent);
+                        } else if (removeLocalContent) {
+                            if (child.isDown()) {
+                                new File(child.getStoragePath()).delete();
+                            }
+                        }
+                    }
+                }
+            }
+            if (removeDBData) {
+                removeFile(dir, true);
+            }
+            
+            if (dir.getFileLength() > 0) {
+                updateSizesToTheRoot(dir.getParentId());
+            }
+        }
+    }
+
+
+    /**
+     * Updates database for a folder that was moved to a different location.
+     * 
+     * TODO explore better (faster) implementations
+     * TODO throw exceptions up !
+     */
+    @Override
+    public void moveDirectory(OCFile dir, String newPath) {
+        // TODO check newPath
+
+        if (dir != null && dir.isDirectory() && dir.fileExists() && !dir.getFileName().equals(OCFile.PATH_SEPARATOR)) {
+            /// 1. get all the descendants of 'dir' in a single QUERY (including 'dir')
+            Cursor c = null;
+            if (getContentProvider() != null) {
+                try {
+                    c = getContentProvider().query(ProviderTableMeta.CONTENT_URI, 
+                            null,
+                            ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + ProviderTableMeta.FILE_PATH + " LIKE ? ",
+                            new String[] { mAccount.name, dir.getRemotePath() + "%"  }, ProviderTableMeta.FILE_PATH + " ASC ");
+                } catch (RemoteException e) {
+                    Log_OC.e(TAG, e.getMessage());
+                }
+            } else {
+                c = getContentResolver().query(ProviderTableMeta.CONTENT_URI, 
+                        null,
+                        ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + ProviderTableMeta.FILE_PATH + " LIKE ? ",
+                        new String[] { mAccount.name, dir.getRemotePath() + "%"  }, ProviderTableMeta.FILE_PATH + " ASC ");
+            }
+
+            /// 2. prepare a batch of update operations to change all the descendants
+            ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(c.getCount());
+            int lengthOfOldPath = dir.getRemotePath().length();
+            String defaultSavePath = FileStorageUtils.getSavePath(mAccount.name);
+            int lengthOfOldStoragePath = defaultSavePath.length() + lengthOfOldPath;
+            if (c.moveToFirst()) {
+                do {
+                    ContentValues cv = new ContentValues(); // don't take the constructor out of the loop and clear the object
+                    OCFile child = createFileInstance(c);
+                    cv.put(ProviderTableMeta.FILE_PATH, newPath + child.getRemotePath().substring(lengthOfOldPath));
+                    if (child.getStoragePath() != null && child.getStoragePath().startsWith(defaultSavePath)) {
+                        cv.put(ProviderTableMeta.FILE_STORAGE_PATH, defaultSavePath + newPath + child.getStoragePath().substring(lengthOfOldStoragePath));
+                    }
+                    operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
+                            withValues(cv).
+                            withSelection(  ProviderTableMeta._ID + "=?", 
+                                    new String[] { String.valueOf(child.getFileId()) })
+                                    .build());
+                } while (c.moveToNext());
+            }
+            c.close();
+
+            /// 3. apply updates in batch
+            try {
+                if (getContentResolver() != null) {
+                    getContentResolver().applyBatch(MainApp.getAuthority(), operations);
+
+                } else {
+                    getContentProvider().applyBatch(operations);
+                }
+
+            } catch (OperationApplicationException e) {
+                Log_OC.e(TAG, "Fail to update descendants of " + dir.getFileId() + " in database", e);
+
+            } catch (RemoteException 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;
+    }
+
+    /**
+     * Calculate and save the folderSize on DB
+     * @param id
+     */
+    @Override
+    public void calculateFolderSize(long id) {
+        long folderSize = 0;
+        
+        Vector<OCFile> files = getDirectoryContent(id);
+        
+        for (OCFile f: files)
+        {
+            folderSize = folderSize + f.getFileLength();
+        }
+        
+        updateSize(id, folderSize);
+    }
+
+    /**
+     * Update the size value of an OCFile in DB
+     */
+    private int updateSize(long id, long size) {
+        ContentValues cv = new ContentValues();
+        cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, size);
+        int result = -1;
+        if (getContentResolver() != null) {
+             result = getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv, ProviderTableMeta._ID + "=?", 
+                     new String[] { String.valueOf(id) });
+        } else {
+            try {
+                result = getContentProvider().update(ProviderTableMeta.CONTENT_URI, cv, ProviderTableMeta._ID + "=?", 
+                        new String[] { String.valueOf(id) });
+            } catch (RemoteException e) {
+                Log_OC.e(TAG,"Fail to update size column into database " + e.getMessage());
+            }
+        }
+        return result;
+    }
+
+    /** 
+     * Update the size of a subtree of folder from a file to the root
+     * @param parentId: parent of the file
+     */
+    private void updateSizesToTheRoot(long parentId) {
+        
+        OCFile file; 
+
+        while (parentId != 0) {
+            
+            // Update the size of the parent
+            calculateFolderSize(parentId);
+            
+            // search the next parent
+            file = getFileById(parentId);            
+            parentId = file.getParentId();
+            
+        }              
+        
+    }
+    
+}
diff --git a/src/com/owncloud/android/datamodel/OCFile.java b/src/com/owncloud/android/datamodel/OCFile.java
new file mode 100644 (file)
index 0000000..76fb6b2
--- /dev/null
@@ -0,0 +1,486 @@
+/* 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.datamodel;
+
+import java.io.File;
+
+import com.owncloud.android.Log_OC;
+
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.webkit.MimeTypeMap;
+
+public class OCFile implements Parcelable, Comparable<OCFile> {
+
+    public static final Parcelable.Creator<OCFile> CREATOR = new Parcelable.Creator<OCFile>() {
+        @Override
+        public OCFile createFromParcel(Parcel source) {
+            return new OCFile(source);
+        }
+
+        @Override
+        public OCFile[] newArray(int size) {
+            return new OCFile[size];
+        }
+    };
+
+    public static final String PATH_SEPARATOR = "/";
+
+    private static final String TAG = OCFile.class.getSimpleName();
+    
+    private long mId;
+    private long mParentId;
+    private long mLength;
+    private long mCreationTimestamp;
+    private long mModifiedTimestamp;
+    private long mModifiedTimestampAtLastSyncForData;
+    private String mRemotePath;
+    private String mLocalPath;
+    private String mMimeType;
+    private boolean mNeedsUpdating;
+    private long mLastSyncDateForProperties;
+    private long mLastSyncDateForData;
+    private boolean mKeepInSync;
+
+    private String mEtag;
+
+    /**
+     * Create new {@link OCFile} with given path.
+     * 
+     * The path received must be URL-decoded. Path separator must be OCFile.PATH_SEPARATOR, and it must be the first character in 'path'.
+     * 
+     * @param path The remote path of the file.
+     */
+    public OCFile(String path) {
+        resetData();
+        mNeedsUpdating = false;
+        if (path == null || path.length() <= 0 || !path.startsWith(PATH_SEPARATOR)) {
+            throw new IllegalArgumentException("Trying to create a OCFile with a non valid remote path: " + path);
+        }
+        mRemotePath = path;
+    }
+
+    /**
+     * Reconstruct from parcel
+     * 
+     * @param source The source parcel
+     */
+    private OCFile(Parcel source) {
+        mId = source.readLong();
+        mParentId = source.readLong();
+        mLength = source.readLong();
+        mCreationTimestamp = source.readLong();
+        mModifiedTimestamp = source.readLong();
+        mModifiedTimestampAtLastSyncForData = source.readLong();
+        mRemotePath = source.readString();
+        mLocalPath = source.readString();
+        mMimeType = source.readString();
+        mNeedsUpdating = source.readInt() == 0;
+        mKeepInSync = source.readInt() == 1;
+        mLastSyncDateForProperties = source.readLong();
+        mLastSyncDateForData = source.readLong();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeLong(mId);
+        dest.writeLong(mParentId);
+        dest.writeLong(mLength);
+        dest.writeLong(mCreationTimestamp);
+        dest.writeLong(mModifiedTimestamp);
+        dest.writeLong(mModifiedTimestampAtLastSyncForData);
+        dest.writeString(mRemotePath);
+        dest.writeString(mLocalPath);
+        dest.writeString(mMimeType);
+        dest.writeInt(mNeedsUpdating ? 1 : 0);
+        dest.writeInt(mKeepInSync ? 1 : 0);
+        dest.writeLong(mLastSyncDateForProperties);
+        dest.writeLong(mLastSyncDateForData);
+    }
+    
+    /**
+     * Gets the ID of the file
+     * 
+     * @return the file ID
+     */
+    public long getFileId() {
+        return mId;
+    }
+
+    /**
+     * Returns the remote path of the file on ownCloud
+     * 
+     * @return The remote path to the file
+     */
+    public String getRemotePath() {
+        return mRemotePath;
+    }
+
+    /**
+     * Can be used to check, whether or not this file exists in the database
+     * already
+     * 
+     * @return true, if the file exists in the database
+     */
+    public boolean fileExists() {
+        return mId != -1;
+    }
+
+    /**
+     * Use this to find out if this file is a Directory
+     * 
+     * @return true if it is a directory
+     */
+    public boolean isDirectory() {
+        return mMimeType != null && mMimeType.equals("DIR");
+    }
+
+    /**
+     * Use this to check if this file is available locally
+     * 
+     * @return true if it is
+     */
+    public boolean isDown() {
+        if (mLocalPath != null && mLocalPath.length() > 0) {
+            File file = new File(mLocalPath);
+            return (file.exists());
+        }
+        return false;
+    }
+    
+    /**
+     * The path, where the file is stored locally
+     * 
+     * @return The local path to the file
+     */
+    public String getStoragePath() {
+        return mLocalPath;
+    }
+
+    /**
+     * Can be used to set the path where the file is stored
+     * 
+     * @param storage_path to set
+     */
+    public void setStoragePath(String storage_path) {
+        mLocalPath = storage_path;
+    }
+
+    /**
+     * Get a UNIX timestamp of the file creation time
+     * 
+     * @return A UNIX timestamp of the time that file was created
+     */
+    public long getCreationTimestamp() {
+        return mCreationTimestamp;
+    }
+
+    /**
+     * Set a UNIX timestamp of the time the file was created
+     * 
+     * @param creation_timestamp to set
+     */
+    public void setCreationTimestamp(long creation_timestamp) {
+        mCreationTimestamp = creation_timestamp;
+    }
+
+    /**
+     * Get a UNIX timestamp of the file modification time.
+     *
+     * @return  A UNIX timestamp of the modification time, corresponding to the value returned by the server
+     *          in the last synchronization of the properties of this file. 
+     */
+    public long getModificationTimestamp() {
+        return mModifiedTimestamp;
+    }
+
+    /**
+     * Set a UNIX timestamp of the time the time the file was modified.
+     * 
+     * To update with the value returned by the server in every synchronization of the properties 
+     * of this file.
+     * 
+     * @param modification_timestamp to set
+     */
+    public void setModificationTimestamp(long modification_timestamp) {
+        mModifiedTimestamp = modification_timestamp;
+    }
+
+    
+    /**
+     * Get a UNIX timestamp of the file modification time.
+     *
+     * @return  A UNIX timestamp of the modification time, corresponding to the value returned by the server
+     *          in the last synchronization of THE CONTENTS of this file. 
+     */
+    public long getModificationTimestampAtLastSyncForData() {
+        return mModifiedTimestampAtLastSyncForData;
+    }
+
+    /**
+     * Set a UNIX timestamp of the time the time the file was modified.
+     * 
+     * To update with the value returned by the server in every synchronization of THE CONTENTS 
+     * of this file.
+     * 
+     * @param modification_timestamp to set
+     */
+    public void setModificationTimestampAtLastSyncForData(long modificationTimestamp) {
+        mModifiedTimestampAtLastSyncForData = modificationTimestamp;
+    }
+
+    
+    
+    /**
+     * Returns the filename and "/" for the root directory
+     * 
+     * @return The name of the file
+     */
+    public String getFileName() {
+        File f = new File(getRemotePath());
+        return f.getName().length() == 0 ? PATH_SEPARATOR : f.getName();
+    }
+    
+    /**
+     * Sets the name of the file
+     * 
+     * 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_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;
+            mRemotePath =  parent + name;
+            if (isDirectory()) {
+                mRemotePath += PATH_SEPARATOR;
+            }
+            Log_OC.d(TAG, "OCFile name changed to " + mRemotePath);
+        }
+    }
+
+    /**
+     * Can be used to get the Mimetype
+     * 
+     * @return the Mimetype as a String
+     */
+    public String getMimetype() {
+        return mMimeType;
+    }
+
+    /**
+     * Adds a file to this directory. If this file is not a directory, an
+     * exception gets thrown.
+     * 
+     * @param file to add
+     * @throws IllegalStateException if you try to add a something and this is
+     *             not a directory
+     */
+    public void addFile(OCFile file) throws IllegalStateException {
+        if (isDirectory()) {
+            file.mParentId = mId;
+            mNeedsUpdating = true;
+            return;
+        }
+        throw new IllegalStateException(
+                "This is not a directory where you can add stuff to!");
+    }
+
+    /**
+     * Used internally. Reset all file properties
+     */
+    private void resetData() {
+        mId = -1;
+        mRemotePath = null;
+        mParentId = 0;
+        mLocalPath = null;
+        mMimeType = null;
+        mLength = 0;
+        mCreationTimestamp = 0;
+        mModifiedTimestamp = 0;
+        mModifiedTimestampAtLastSyncForData = 0;
+        mLastSyncDateForProperties = 0;
+        mLastSyncDateForData = 0;
+        mKeepInSync = false;
+        mNeedsUpdating = false;
+    }
+
+    /**
+     * Sets the ID of the file
+     * 
+     * @param file_id to set
+     */
+    public void setFileId(long file_id) {
+        mId = file_id;
+    }
+
+    /**
+     * Sets the Mime-Type of the
+     * 
+     * @param mimetype to set
+     */
+    public void setMimetype(String mimetype) {
+        mMimeType = mimetype;
+    }
+
+    /**
+     * Sets the ID of the parent folder
+     * 
+     * @param parent_id to set
+     */
+    public void setParentId(long parent_id) {
+        mParentId = parent_id;
+    }
+
+    /**
+     * Sets the file size in bytes
+     * 
+     * @param file_len to set
+     */
+    public void setFileLength(long file_len) {
+        mLength = file_len;
+    }
+
+    /**
+     * Returns the size of the file in bytes
+     * 
+     * @return The filesize in bytes
+     */
+    public long getFileLength() {
+        return mLength;
+    }
+
+    /**
+     * Returns the ID of the parent Folder
+     * 
+     * @return The ID
+     */
+    public long getParentId() {
+        return mParentId;
+    }
+
+    /**
+     * Check, if this file needs updating
+     * 
+     * @return
+     */
+    public boolean needsUpdatingWhileSaving() {
+        return mNeedsUpdating;
+    }
+    
+    public long getLastSyncDateForProperties() {
+        return mLastSyncDateForProperties;
+    }
+    
+    public void setLastSyncDateForProperties(long lastSyncDate) {
+        mLastSyncDateForProperties = lastSyncDate;
+    }
+    
+    public long getLastSyncDateForData() {
+        return mLastSyncDateForData;
+    }
+
+    public void setLastSyncDateForData(long lastSyncDate) {
+        mLastSyncDateForData = lastSyncDate;
+    }
+
+    public void setKeepInSync(boolean keepInSync) {
+        mKeepInSync = keepInSync;
+    }
+    
+    public boolean keepInSync() {
+        return mKeepInSync;
+    }
+    
+    @Override
+    public int describeContents() {
+        return this.hashCode();
+    }
+
+    @Override
+    public int compareTo(OCFile another) {
+        if (isDirectory() && another.isDirectory()) {
+            return getRemotePath().toLowerCase().compareTo(another.getRemotePath().toLowerCase());
+        } else if (isDirectory()) {
+            return -1;
+        } else if (another.isDirectory()) {
+            return 1;
+        }
+        return getRemotePath().toLowerCase().compareTo(another.getRemotePath().toLowerCase());
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if(o instanceof OCFile){
+            OCFile that = (OCFile) o;
+            if(that != null){
+                return this.mId == that.mId;
+            }
+        }
+        
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        String asString = "[id=%s, name=%s, mime=%s, downloaded=%s, local=%s, remote=%s, parentId=%s, keepInSinc=%s]";
+        asString = String.format(asString, Long.valueOf(mId), getFileName(), mMimeType, isDown(), mLocalPath, mRemotePath, Long.valueOf(mParentId), Boolean.valueOf(mKeepInSync));
+        return asString;
+    }
+
+    public String getEtag() {
+        return mEtag;
+    }
+
+    public long getLocalModificationTimestamp() {
+        if (mLocalPath != null && mLocalPath.length() > 0) {
+            File f = new File(mLocalPath);
+            return f.lastModified();
+        }
+        return 0;
+    }
+
+    /** @return  'True' if the file contains audio */
+    public boolean isAudio() {
+        return (mMimeType != null && mMimeType.startsWith("audio/"));
+    }
+
+    /** @return  'True' if the file contains video */
+    public boolean isVideo() {
+        return (mMimeType != null && mMimeType.startsWith("video/"));
+    }
+
+    /** @return  'True' if the file contains an image */
+    public boolean isImage() {
+        return ((mMimeType != null && mMimeType.startsWith("image/")) ||
+                 getMimeTypeFromName().startsWith("image/"));
+    }
+    
+    public String getMimeTypeFromName() {
+        String extension = "";
+        int pos = mRemotePath.lastIndexOf('.');
+        if (pos >= 0) {
+            extension = mRemotePath.substring(pos + 1);
+        }
+        String result = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase());
+        return (result != null) ? result : "";
+    }
+
+}
diff --git a/src/com/owncloud/android/db/DbHandler.java b/src/com/owncloud/android/db/DbHandler.java
new file mode 100644 (file)
index 0000000..8f53562
--- /dev/null
@@ -0,0 +1,120 @@
+/* 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 com.owncloud.android.MainApp;
+
+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();
+        mDatabaseName = MainApp.getDBName();
+    }
+
+    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;");
+
+        }
+    }
+}
diff --git a/src/com/owncloud/android/db/ProviderMeta.java b/src/com/owncloud/android/db/ProviderMeta.java
new file mode 100644 (file)
index 0000000..a082a84
--- /dev/null
@@ -0,0 +1,73 @@
+/* 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 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
+package com.owncloud.android.db;\r
+\r
+import com.owncloud.android.MainApp;\r
+\r
+import android.net.Uri;\r
+import android.provider.BaseColumns;\r
+\r
+/**\r
+ * Meta-Class that holds various static field information\r
+ * \r
+ * @author Bartek Przybylski\r
+ * \r
+ */\r
+public class ProviderMeta {\r
+\r
+    /* These constants are now in MainApp\r
+        public static final String AUTHORITY_FILES = "org.owncloud";\r
+        public static final String DB_FILE = "owncloud.db";\r
+    */\r
+    public static final String DB_NAME = "filelist";\r
+    public static final int DB_VERSION = 4;\r
+\r
+    private ProviderMeta() {\r
+    }\r
+\r
+    static public class ProviderTableMeta implements BaseColumns {\r
+        public static final String DB_NAME = "filelist";\r
+        public static final Uri CONTENT_URI = Uri.parse("content://"\r
+                + MainApp.getAuthority() + "/");\r
+        public static final Uri CONTENT_URI_FILE = Uri.parse("content://"\r
+                + MainApp.getAuthority() + "/file");\r
+        public static final Uri CONTENT_URI_DIR = Uri.parse("content://"\r
+                + MainApp.getAuthority() + "/dir");\r
+\r
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.owncloud.file";\r
+        public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/vnd.owncloud.file";\r
+\r
+        public static final String FILE_PARENT = "parent";\r
+        public static final String FILE_NAME = "filename";\r
+        public static final String FILE_CREATION = "created";\r
+        public static final String FILE_MODIFIED = "modified";\r
+        public static final String FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA = "modified_at_last_sync_for_data";\r
+        public static final String FILE_CONTENT_LENGTH = "content_length";\r
+        public static final String FILE_CONTENT_TYPE = "content_type";\r
+        public static final String FILE_STORAGE_PATH = "media_path";\r
+        public static final String FILE_PATH = "path";\r
+        public static final String FILE_ACCOUNT_OWNER = "file_owner";\r
+        public static final String FILE_LAST_SYNC_DATE = "last_sync_date";  // _for_properties, but let's keep it as it is\r
+        public static final String FILE_LAST_SYNC_DATE_FOR_DATA = "last_sync_date_for_data";\r
+        public static final String FILE_KEEP_IN_SYNC = "keep_in_sync";\r
+\r
+        public static final String DEFAULT_SORT_ORDER = FILE_NAME\r
+                + " collate nocase asc";\r
+\r
+    }\r
+}\r
diff --git a/src/com/owncloud/android/extensions/ExtensionsAvailableActivity.java b/src/com/owncloud/android/extensions/ExtensionsAvailableActivity.java
new file mode 100644 (file)
index 0000000..7b39931
--- /dev/null
@@ -0,0 +1,35 @@
+/* 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.extensions;
+
+import android.os.Bundle;
+import android.support.v4.app.FragmentManager;
+
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+
+public class ExtensionsAvailableActivity extends SherlockFragmentActivity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        FragmentManager fm = getSupportFragmentManager();
+        ExtensionsAvailableDialog ead = new ExtensionsAvailableDialog();
+        ead.show(fm, "extensions_available_dialog");
+    }
+}
diff --git a/src/com/owncloud/android/extensions/ExtensionsAvailableDialog.java b/src/com/owncloud/android/extensions/ExtensionsAvailableDialog.java
new file mode 100644 (file)
index 0000000..efd4e01
--- /dev/null
@@ -0,0 +1,70 @@
+/* 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.extensions;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.ui.CustomButton;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.OnClickListener;
+
+public class ExtensionsAvailableDialog extends DialogFragment implements
+        OnClickListener {
+
+    public ExtensionsAvailableDialog() {
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        View view = inflater.inflate(R.layout.extensions_available_dialog,
+                container);
+        CustomButton btnYes = (CustomButton) view.findViewById(R.id.buttonYes);
+        CustomButton btnNo = (CustomButton) view.findViewById(R.id.buttonNo);
+        
+        btnYes.setOnClickListener(this);
+        btnNo.setOnClickListener(this);
+        getDialog().setTitle(R.string.extensions_avail_title);
+        return view;
+    }
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+        case R.id.buttonYes: {
+            Intent i = new Intent(getActivity(), ExtensionsListActivity.class);
+            startActivity(i);
+            getActivity().finish();
+        }
+            break;
+        case R.id.buttonNo:
+            getActivity().finish();
+            break;
+        default:
+            Log_OC.e("EAD", "Button with unknown id clicked " + v.getId());
+        }
+    }
+
+}
diff --git a/src/com/owncloud/android/extensions/ExtensionsListActivity.java b/src/com/owncloud/android/extensions/ExtensionsListActivity.java
new file mode 100644 (file)
index 0000000..02fb4a9
--- /dev/null
@@ -0,0 +1,156 @@
+/* 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.extensions;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.utils.OwnCloudVersion;
+
+
+
+import android.R;
+import android.app.ListActivity;
+import android.os.Bundle;
+import android.os.Handler;
+import android.widget.SimpleAdapter;
+
+public class ExtensionsListActivity extends ListActivity {
+
+    private static final String packages_url = "http://alefzero.eu/a/packages.php";
+
+    private Thread mGetterThread;
+    private final Handler mHandler = new Handler();
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mGetterThread = new Thread(new JsonGetter());
+        mGetterThread.start();
+    }
+
+    public void done(JSONArray a) {
+        LinkedList<HashMap<String, String>> ll = new LinkedList<HashMap<String, String>>();
+        for (int i = 0; i < a.length(); ++i) {
+            try {
+                ExtensionApplicationEntry ela = new ExtensionApplicationEntry(
+                        ((JSONObject) a.get(i)));
+                HashMap<String, String> ss = new HashMap<String, String>();
+                ss.put("NAME", ela.getName());
+                ss.put("DESC", ela.getDescription());
+                ll.add(ss);
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+        }
+        setListAdapter(new SimpleAdapter(this, ll, R.layout.simple_list_item_2,
+                new String[] { "NAME", "DESC" }, new int[] {
+                        android.R.id.text1, android.R.id.text2 }));
+
+    }
+
+    private class JsonGetter implements Runnable {
+
+        @Override
+        public void run() {
+            HttpClient hc = new HttpClient();
+            GetMethod gm = new GetMethod(packages_url);
+            final JSONArray ar;
+            try {
+                hc.executeMethod(gm);
+                Log_OC.e("ASD", gm.getResponseBodyAsString() + "");
+                ar = new JSONObject(gm.getResponseBodyAsString())
+                        .getJSONArray("apps");
+            } catch (Exception e) {
+                e.printStackTrace();
+                return;
+            }
+
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    done(ar);
+                }
+            });
+
+        }
+
+    }
+
+    private class ExtensionApplicationEntry {
+        private static final String APP_NAME = "name";
+        private static final String APP_VERSION = "version";
+        private static final String APP_DESC = "description";
+        private static final String APP_ICON = "icon";
+        private static final String APP_URL = "download";
+        private static final String APP_PLAYID = "play_id";
+
+        private String mName, mDescription, mIcon, mDownload, mPlayId;
+        private OwnCloudVersion mVersion;
+
+        public ExtensionApplicationEntry(JSONObject appentry) {
+            try {
+                mName = appentry.getString(APP_NAME);
+                mDescription = appentry.getString(APP_DESC);
+                mIcon = appentry.getString(APP_ICON);
+                mDownload = appentry.getString(APP_URL);
+                mPlayId = appentry.getString(APP_PLAYID);
+                mVersion = new OwnCloudVersion(appentry.getString(APP_VERSION));
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+        }
+
+        public String getName() {
+            return mName;
+        }
+
+        public String getDescription() {
+            return mDescription;
+        }
+
+        @SuppressWarnings("unused")
+        public String getIcon() {
+            return mIcon;
+        }
+
+        @SuppressWarnings("unused")
+        public String getDownload() {
+            return mDownload;
+        }
+
+        @SuppressWarnings("unused")
+        public String getPlayId() {
+            return mPlayId;
+        }
+
+        @SuppressWarnings("unused")
+        public OwnCloudVersion getVersion() {
+            return mVersion;
+        }
+    }
+    
+}
diff --git a/src/com/owncloud/android/files/BootupBroadcastReceiver.java b/src/com/owncloud/android/files/BootupBroadcastReceiver.java
new file mode 100644 (file)
index 0000000..8a8c430
--- /dev/null
@@ -0,0 +1,46 @@
+/* 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;
+
+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;
+
+public class BootupBroadcastReceiver extends BroadcastReceiver {
+
+    private static String TAG = "BootupBroadcastReceiver";
+    
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (!intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
+            Log_OC.wtf(TAG, "Incorrect action sent " + intent.getAction());
+            return;
+        }
+        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_OC.d(TAG, "DONE");
+    }
+
+}
diff --git a/src/com/owncloud/android/files/FileHandler.java b/src/com/owncloud/android/files/FileHandler.java
new file mode 100644 (file)
index 0000000..2eb754d
--- /dev/null
@@ -0,0 +1,30 @@
+/* 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.files;
+
+import com.owncloud.android.datamodel.OCFile;
+
+public interface FileHandler {
+
+    /**
+     * TODO
+     */
+    public void openFile(OCFile file);
+
+    
+}
diff --git a/src/com/owncloud/android/files/InstantUploadBroadcastReceiver.java b/src/com/owncloud/android/files/InstantUploadBroadcastReceiver.java
new file mode 100644 (file)
index 0000000..8d453f6
--- /dev/null
@@ -0,0 +1,215 @@
+/* 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;
+
+import java.io.File;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.db.DbHandler;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.utils.FileStorageUtils;
+
+
+import android.accounts.Account;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+//import android.content.IntentFilter;
+import android.database.Cursor;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo.State;
+import android.preference.PreferenceManager;
+import android.provider.MediaStore.Images.Media;
+import android.webkit.MimeTypeMap;
+
+
+public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
+
+    private static String TAG = "InstantUploadBroadcastReceiver";
+    private static final String[] CONTENT_PROJECTION = { Media.DATA, Media.DISPLAY_NAME, Media.MIME_TYPE, Media.SIZE };
+    //Unofficial action, works for most devices but not HTC. See: https://github.com/owncloud/android/issues/6
+    private static String NEW_PHOTO_ACTION_UNOFFICIAL = "com.android.camera.NEW_PICTURE";
+    //Officially supported action since SDK 14: http://developer.android.com/reference/android/hardware/Camera.html#ACTION_NEW_PICTURE
+    private static String NEW_PHOTO_ACTION = "android.hardware.action.NEW_PICTURE";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Log_OC.d(TAG, "Received: " + intent.getAction());
+        
+        FileUploader fileUploader = new FileUploader();
+        
+        if (intent.getAction().equals(android.net.ConnectivityManager.CONNECTIVITY_ACTION)) {
+            handleConnectivityAction(context, intent);
+        }else if (intent.getAction().equals(NEW_PHOTO_ACTION_UNOFFICIAL)) {
+            handleNewPhotoAction(context, intent);
+            Log_OC.d(TAG, "UNOFFICIAL processed: com.android.camera.NEW_PICTURE");
+        } else if (intent.getAction().equals(NEW_PHOTO_ACTION)) {
+            handleNewPhotoAction(context, intent);
+            Log_OC.d(TAG, "OFFICIAL processed: android.hardware.action.NEW_PICTURE");
+        } else if (intent.getAction().equals(fileUploader.getUploadFinishMessage())) {
+            handleUploadFinished(context, intent);
+        } else {
+            Log_OC.e(TAG, "Incorrect intent sent: " + intent.getAction());
+        }
+    }
+
+    private void handleUploadFinished(Context context, Intent intent) {
+        // remove successfull uploading, ignore rest for reupload on reconnect
+        /*
+        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)) {
+                Log_OC.w(TAG, "Tried to remove non existing instant upload file " + localPath);
+            }
+            db.close();
+        }
+        */
+    }
+
+    private void handleNewPhotoAction(Context context, Intent intent) {
+        if (!instantUploadEnabled(context)) {
+            Log_OC.d(TAG, "Instant upload disabled, aborting uploading");
+            return;
+        }
+
+        Account account = AccountUtils.getCurrentOwnCloudAccount(context);
+        if (account == null) {
+            Log_OC.w(TAG, "No owncloud account found for instant upload, aborting");
+            return;
+        }
+
+        Cursor c = context.getContentResolver().query(intent.getData(), CONTENT_PROJECTION, null, null, null);
+
+        if (!c.moveToFirst()) {
+            Log_OC.e(TAG, "Couldn't resolve given uri: " + intent.getDataString());
+            return;
+        }
+
+        String file_path = c.getString(c.getColumnIndex(Media.DATA));
+        String file_name = c.getString(c.getColumnIndex(Media.DISPLAY_NAME));
+        String mime_type = c.getString(c.getColumnIndex(Media.MIME_TYPE));
+
+        c.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
+        // 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, 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);
+        context.startService(i);
+
+    }
+
+    private void handleConnectivityAction(Context context, Intent intent) {
+        if (!instantUploadEnabled(context)) {
+            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))) {
+            DbHandler db = new DbHandler(context);
+            Cursor c = db.getAwaitingFiles();
+            if (c.moveToFirst()) {
+                //IntentFilter filter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE);
+                //context.getApplicationContext().registerReceiver(this, filter);
+                do {
+                    String account_name = c.getString(c.getColumnIndex("account"));
+                    String file_path = c.getString(c.getColumnIndex("path"));
+                    File f = new File(file_path);
+                    if (f.exists()) {
+                        Account account = new Account(account_name, MainApp.getAccountType());
+
+                        String mimeType = null;
+                        try {
+                            mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
+                                    f.getName().substring(f.getName().lastIndexOf('.') + 1));
+
+                        } catch (Throwable e) {
+                            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, 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_OC.w(TAG, "Instant upload file " + f.getAbsolutePath() + " dont exist anymore");
+                    }
+                } while (c.moveToNext());
+            }
+            c.close();
+            db.close();
+        }
+
+    }
+
+    public static boolean isOnline(Context context) {
+        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        return cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnected();
+    }
+
+    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;
+    }
+
+    public static boolean instantUploadEnabled(Context context) {
+        return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_uploading", false);
+    }
+
+    public static boolean instantUploadViaWiFiOnly(Context context) {
+        return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_upload_on_wifi", false);
+    }
+}
diff --git a/src/com/owncloud/android/files/OwnCloudFileObserver.java b/src/com/owncloud/android/files/OwnCloudFileObserver.java
new file mode 100644 (file)
index 0000000..8f2e3ee
--- /dev/null
@@ -0,0 +1,103 @@
+/* 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;
+
+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;
+import com.owncloud.android.operations.SynchronizeFileOperation;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.ui.activity.ConflictsResolveActivity;
+
+
+
+import android.accounts.Account;
+import android.content.Context;
+import android.content.Intent;
+import android.os.FileObserver;
+
+public class OwnCloudFileObserver extends FileObserver {
+
+    public static int CHANGES_ONLY = CLOSE_WRITE;
+    
+    private static String TAG = OwnCloudFileObserver.class.getSimpleName();
+    
+    private String mPath;
+    private int mMask;
+    private Account mOCAccount;
+    //private OCFile mFile;
+    private Context mContext;
+
+    
+    public OwnCloudFileObserver(String path, Account account, Context context, int mask) {
+        super(path, mask);
+        if (path == null)
+            throw new IllegalArgumentException("NULL path argument received"); 
+        /*if (file == null)
+            throw new IllegalArgumentException("NULL file argument received");*/ 
+        if (account == null)
+            throw new IllegalArgumentException("NULL account argument received"); 
+        if (context == null)
+            throw new IllegalArgumentException("NULL context argument received");
+        /*if (!path.equals(file.getStoragePath()) && !path.equals(FileStorageUtils.getDefaultSavePathFor(account.name, file)))
+            throw new IllegalArgumentException("File argument is not linked to the local file set in path argument"); */
+        mPath = path;
+        //mFile = file;
+        mOCAccount = account;
+        mContext = context; 
+        mMask = mask;
+    }
+    
+    @Override
+    public void onEvent(int event, String path) {
+        Log_OC.d(TAG, "Got file modified with event " + event + " and path " + mPath + ((path != null) ? File.separator + path : ""));
+        if ((event & mMask) == 0) {
+            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;
+        }
+        FileDataStorageManager storageManager = new FileDataStorageManager(mOCAccount, mContext.getContentResolver());
+        OCFile file = storageManager.getFileByLocalPath(mPath);     // a fresh object is needed; many things could have occurred to the file since it was registered to observe
+                                                                    // again, assuming that local files are linked to a remote file AT MOST, SOMETHING TO BE DONE; 
+        SynchronizeFileOperation sfo = new SynchronizeFileOperation(file, 
+                                                                    null, 
+                                                                    storageManager, 
+                                                                    mOCAccount, 
+                                                                    true, 
+                                                                    true, 
+                                                                    mContext);
+        RemoteOperationResult result = sfo.execute(mOCAccount, mContext);
+        if (result.getCode() == ResultCode.SYNC_CONFLICT) {
+            // ISSUE 5: if the user is not running the app (this is a service!), this can be very intrusive; a notification should be preferred
+            Intent i = new Intent(mContext, ConflictsResolveActivity.class);
+            i.setFlags(i.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
+            i.putExtra(ConflictsResolveActivity.EXTRA_FILE, file);
+            i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, mOCAccount);
+            mContext.startActivity(i);
+        }
+        // TODO save other errors in some point where the user can inspect them later;
+        //      or maybe just toast them;
+        //      or nothing, very strange fails
+    }
+    
+}
diff --git a/src/com/owncloud/android/files/managers/OCNotificationManager.java b/src/com/owncloud/android/files/managers/OCNotificationManager.java
new file mode 100644 (file)
index 0000000..11a8c37
--- /dev/null
@@ -0,0 +1,155 @@
+/* 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.managers;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.owncloud.android.R;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.widget.RemoteViews;
+
+
+public class OCNotificationManager {
+
+    enum NotificationType {
+        NOTIFICATION_SIMPLE,
+        NOTIFICATION_PROGRESS
+    }
+    
+    static public class NotificationData {
+        private String mText, mSubtitle;
+        private int mPercent;
+        private boolean mOngoing;
+
+        public NotificationData(String text, String subtitle, boolean ongoing) {
+            this(text, subtitle, -1, ongoing);
+        }
+        
+        public NotificationData(int percent, boolean ongoing) {
+            this(null, null, percent, ongoing);
+        }
+        
+        public NotificationData(String text, int percent, boolean ongoing) {
+            this(text, null, percent, ongoing);
+        }
+        
+        public NotificationData(String text, String subtitle, int percent, boolean ongoing) {
+            mText = text;
+            mPercent = percent;
+            mSubtitle = subtitle;
+            mOngoing = ongoing;
+        }
+        
+        public String getText() { return mText; }
+        public int getPercent() { return mPercent; }
+        public String getSubtitle() { return mSubtitle; }
+        public boolean getOngoing() { return mOngoing; }
+    }
+    
+    static private OCNotificationManager mInstance = null;
+
+    private class NotificationTypePair {
+        public Notification mNotificaiton;
+        public NotificationType mType;
+        public NotificationTypePair(Notification n, NotificationType type) {
+            mNotificaiton = n;
+            mType = type;
+        }
+    }
+    
+    private Context mContext;
+    private Map<Integer, NotificationTypePair> mNotificationMap;
+    private int mNotificationCounter;
+    NotificationManager mNM;
+    
+    static OCNotificationManager getInstance(Context context) {
+        if (mInstance == null)
+            mInstance = new OCNotificationManager(context);
+        return mInstance;
+    }
+    
+    OCNotificationManager(Context context) {
+        mContext = context;
+        mNotificationMap = new HashMap<Integer, NotificationTypePair>();
+        mNM = (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+        mNotificationCounter = 0;
+    }
+    
+    public int postNotification(NotificationType type, NotificationData data) {
+        mNotificationCounter++;
+        Notification notification = null;
+        
+        switch (type) {
+            case NOTIFICATION_SIMPLE:
+                notification = new Notification(R.drawable.icon, data.getText(), System.currentTimeMillis());
+                break;
+            case NOTIFICATION_PROGRESS:
+                notification = new Notification();
+                notification.contentView = new RemoteViews(mContext.getPackageName(), R.layout.progressbar_layout);
+                notification.contentView.setTextViewText(R.id.status_text,
+                                                         data.getText());
+                notification.contentView.setImageViewResource(R.id.status_icon,
+                                                              R.id.icon);
+                notification.contentView.setProgressBar(R.id.status_progress,
+                                                        100,
+                                                        data.getPercent(),
+                                                        false);
+                break;
+            default:
+                return -1;
+        }
+        if (data.getOngoing()) {
+            notification.flags |= notification.flags | Notification.FLAG_ONGOING_EVENT;
+        }
+        
+        mNotificationMap.put(mNotificationCounter, new NotificationTypePair(notification, type));
+        return mNotificationCounter;
+    }
+    
+    public boolean updateNotification(int notification_id, NotificationData data) {
+        if (!mNotificationMap.containsKey(notification_id)) {
+            return false;
+        }
+        NotificationTypePair pair = mNotificationMap.get(notification_id);
+        switch (pair.mType) {
+            case NOTIFICATION_PROGRESS:
+                pair.mNotificaiton.contentView.setProgressBar(R.id.status_text,
+                                                              100,
+                                                              data.getPercent(),
+                                                              false);
+                return true;
+            case NOTIFICATION_SIMPLE:
+                pair.mNotificaiton = new Notification(R.drawable.icon,
+                                                      data.getText(), System.currentTimeMillis());
+                mNM.notify(notification_id, pair.mNotificaiton);
+                return true;
+            default:
+                return false;
+        }
+    }
+    
+    public void discardNotification(int notification_id) {
+        mNM.cancel(notification_id);
+        mNotificationMap.remove(notification_id);
+    }
+}
diff --git a/src/com/owncloud/android/files/services/FileDownloader.java b/src/com/owncloud/android/files/services/FileDownloader.java
new file mode 100644 (file)
index 0000000..64d5d3e
--- /dev/null
@@ -0,0 +1,549 @@
+/* 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.Log_OC;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AuthenticatorActivity;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+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.FileActivity;
+import com.owncloud.android.ui.activity.FileDisplayActivity;
+import com.owncloud.android.ui.preview.PreviewImageActivity;
+import com.owncloud.android.ui.preview.PreviewImageFragment;
+
+import eu.alefzero.webdav.OnDatatransferProgressListener;
+
+
+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 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";
+    
+    private static final String DOWNLOAD_ADDED_MESSAGE = "DOWNLOAD_ADDED";
+    private 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;
+    
+    
+    public String getDownloadAddedMessage() {
+        return getClass().getName().toString() + DOWNLOAD_ADDED_MESSAGE;
+    }
+    
+    public String getDownloadFinishMessage() {
+        return getClass().getName().toString() + DOWNLOAD_FINISH_MESSAGE;
+    }
+    
+    /**
+     * 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, FileDisplayActivity.class);
+        }
+        showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, download.getFile());
+        showDetailsIntent.putExtra(FileActivity.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 ||
+                                                // (downloadResult.isTemporalRedirection() && downloadResult.isIdPRedirection()
+                                                  (downloadResult.isIdPRedirection()
+                                                        && MainApp.getAuthTokenTypeSamlSessionCookie().equals(mDownloadClient.getAuthTokenType())));
+            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_ENFORCED_UPDATE, true);
+                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, FileDisplayActivity.class);
+                    }
+                    showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, download.getFile());
+                    showDetailsIntent.putExtra(FileActivity.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(getDownloadFinishMessage());
+        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(getDownloadAddedMessage());
+        added.putExtra(ACCOUNT_NAME, download.getAccount().name);
+        added.putExtra(EXTRA_REMOTE_PATH, download.getRemotePath());
+        added.putExtra(EXTRA_FILE_PATH, download.getSavePath());
+        sendStickyBroadcast(added);
+    }
+
+}
diff --git a/src/com/owncloud/android/files/services/FileObserverService.java b/src/com/owncloud/android/files/services/FileObserverService.java
new file mode 100644 (file)
index 0000000..a0003be
--- /dev/null
@@ -0,0 +1,285 @@
+/* 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.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;
+import com.owncloud.android.files.OwnCloudFileObserver;
+import com.owncloud.android.operations.SynchronizeFileOperation;
+import com.owncloud.android.utils.FileStorageUtils;
+
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.Cursor;
+import android.os.Binder;
+import android.os.IBinder;
+
+public class FileObserverService extends Service {
+
+    public final static int CMD_INIT_OBSERVED_LIST = 1;
+    public final static int CMD_ADD_OBSERVED_FILE = 2;
+    public final static int CMD_DEL_OBSERVED_FILE = 3;
+
+    public final static String KEY_FILE_CMD = "KEY_FILE_CMD";
+    public final static String KEY_CMD_ARG_FILE = "KEY_CMD_ARG_FILE";
+    public final static String KEY_CMD_ARG_ACCOUNT = "KEY_CMD_ARG_ACCOUNT";
+
+    private static String TAG = FileObserverService.class.getSimpleName();
+
+    private static Map<String, OwnCloudFileObserver> mObserversMap;
+    private static DownloadCompletedReceiverBis mDownloadReceiver;
+    private IBinder mBinder = new LocalBinder();
+    
+    private String mDownloadAddedMessage;
+    private String mDownloadFinishMessage;
+
+    public class LocalBinder extends Binder {
+        FileObserverService getService() {
+            return FileObserverService.this;
+        }
+    }
+    
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mDownloadReceiver = new DownloadCompletedReceiverBis();
+        
+        FileDownloader downloader = new FileDownloader();
+        mDownloadAddedMessage = downloader.getDownloadAddedMessage();
+        mDownloadFinishMessage= downloader.getDownloadFinishMessage();
+        
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(mDownloadAddedMessage);
+        filter.addAction(mDownloadFinishMessage);        
+        registerReceiver(mDownloadReceiver, filter);
+        
+        mObserversMap = new HashMap<String, OwnCloudFileObserver>();
+        //initializeObservedList();
+    }
+    
+    
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        unregisterReceiver(mDownloadReceiver);
+        mObserversMap = null;   // TODO study carefully the life cycle of Services to grant the best possible observance
+        Log_OC.d(TAG, "Bye, bye");
+    }
+    
+    
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        // this occurs when system tries to restart
+        // service, so we need to reinitialize observers
+        if (intent == null) {
+            initializeObservedList();
+            return Service.START_STICKY;
+        }
+            
+        if (!intent.hasExtra(KEY_FILE_CMD)) {
+            Log_OC.e(TAG, "No KEY_FILE_CMD argument given");
+            return Service.START_STICKY;
+        }
+
+        switch (intent.getIntExtra(KEY_FILE_CMD, -1)) {
+            case CMD_INIT_OBSERVED_LIST:
+                initializeObservedList();
+                break;
+            case CMD_ADD_OBSERVED_FILE:
+                addObservedFile( (OCFile)intent.getParcelableExtra(KEY_CMD_ARG_FILE), 
+                                 (Account)intent.getParcelableExtra(KEY_CMD_ARG_ACCOUNT));
+                break;
+            case CMD_DEL_OBSERVED_FILE:
+                removeObservedFile( (OCFile)intent.getParcelableExtra(KEY_CMD_ARG_FILE), 
+                                    (Account)intent.getParcelableExtra(KEY_CMD_ARG_ACCOUNT));
+                break;
+            default:
+                Log_OC.wtf(TAG, "Incorrect key given");
+        }
+
+        return Service.START_STICKY;
+    }
+
+    
+    /**
+     * Read from the local database the list of files that must to be kept synchronized and 
+     * starts file observers to monitor local changes on them
+     */
+    private void initializeObservedList() {
+        mObserversMap.clear();
+        Cursor c = getContentResolver().query(
+                ProviderTableMeta.CONTENT_URI,
+                null,
+                ProviderTableMeta.FILE_KEEP_IN_SYNC + " = ?",
+                new String[] {String.valueOf(1)},
+                null);
+        if (c == null || !c.moveToFirst()) return;
+        AccountManager acm = AccountManager.get(this);
+        Account[] accounts = acm.getAccounts();
+        do {
+            Account account = null;
+            for (Account a : accounts)
+                if (a.name.equals(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ACCOUNT_OWNER)))) {
+                    account = a;
+                    break;
+                }
+
+            if (account == null) continue;
+            FileDataStorageManager storage =
+                    new FileDataStorageManager(account, getContentResolver());
+            if (!storage.fileExists(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PATH))))
+                continue;
+
+            String path = c.getString(c.getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH));
+            if (path == null || path.length() <= 0)
+                continue;
+            OwnCloudFileObserver observer =
+                    new OwnCloudFileObserver(   path, 
+                                                account, 
+                                                getApplicationContext(), 
+                                                OwnCloudFileObserver.CHANGES_ONLY);
+            mObserversMap.put(path, observer);
+            if (new File(path).exists()) {
+                observer.startWatching();
+                Log_OC.d(TAG, "Started watching file " + path);
+            }
+            
+        } while (c.moveToNext());
+        c.close();
+    }
+    
+    
+    /**
+     * Registers the local copy of a remote file to be observed for local changes,
+     * an automatically updated in the ownCloud server.
+     * 
+     * This method does NOT perform a {@link SynchronizeFileOperation} over the file. 
+     *
+     * TODO We are ignoring that, currently, a local file can be linked to different files
+     * in ownCloud if it's uploaded several times. That's something pending to update: we 
+     * will avoid that the same local file is linked to different remote files.
+     * 
+     * @param file      Object representing a remote file which local copy must be observed.
+     * @param account   OwnCloud account containing file.
+     */
+    private void addObservedFile(OCFile file, Account account) {
+        if (file == null) {
+            Log_OC.e(TAG, "Trying to add a NULL file to observer");
+            return;
+        }
+        String localPath = file.getStoragePath();
+        if (localPath == null || localPath.length() <= 0) { // file downloading / to be download for the first time
+            localPath = FileStorageUtils.getDefaultSavePathFor(account.name, file);
+        }
+        OwnCloudFileObserver observer = mObserversMap.get(localPath);
+        if (observer == null) {
+            /// the local file was never registered to observe before
+            observer = new OwnCloudFileObserver(    localPath, 
+                                                    account, 
+                                                    getApplicationContext(), 
+                                                    OwnCloudFileObserver.CHANGES_ONLY);
+            mObserversMap.put(localPath, observer);
+            Log_OC.d(TAG, "Observer added for path " + localPath);
+        
+            if (file.isDown()) {
+                observer.startWatching();
+                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
+        }
+        
+    }
+
+    
+    /**
+     * Unregisters the local copy of a remote file to be observed for local changes.
+     *
+     * Starts to watch it, if the file has a local copy to watch.
+     * 
+     * TODO We are ignoring that, currently, a local file can be linked to different files
+     * in ownCloud if it's uploaded several times. That's something pending to update: we 
+     * will avoid that the same local file is linked to different remote files.
+     *
+     * @param file      Object representing a remote file which local copy must be not observed longer.
+     * @param account   OwnCloud account containing file.
+     */
+    private void removeObservedFile(OCFile file, Account account) {
+        if (file == null) {
+            Log_OC.e(TAG, "Trying to remove a NULL file");
+            return;
+        }
+        String localPath = file.getStoragePath();
+        if (localPath == null || localPath.length() <= 0) {
+            localPath = FileStorageUtils.getDefaultSavePathFor(account.name, file);
+        }
+        
+        OwnCloudFileObserver observer = mObserversMap.get(localPath);
+        if (observer != null) {
+            observer.stopWatching();
+            mObserversMap.remove(observer);
+            Log_OC.d(TAG, "Stopped watching " + localPath);
+        }
+        
+    }
+
+
+    /**
+     *  Private receiver listening to events broadcast by the FileDownloader service.
+     * 
+     *  Starts and stops the observance on registered files when they are being download,
+     *  in order to avoid to start unnecessary synchronizations. 
+     */
+    private class DownloadCompletedReceiverBis extends BroadcastReceiver {
+        
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String downloadPath = intent.getStringExtra(FileDownloader.EXTRA_FILE_PATH);
+            OwnCloudFileObserver observer = mObserversMap.get(downloadPath);
+            if (observer != null) {
+                if (intent.getAction().equals(mDownloadFinishMessage) &&
+                        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_OC.d(TAG, "Watching again " + downloadPath);
+                
+                } else if (intent.getAction().equals(mDownloadAddedMessage)) {
+                    observer.stopWatching();
+                    Log_OC.d(TAG, "Disabling observance of " + downloadPath);
+                } 
+            }
+        }
+        
+    }
+    
+}
diff --git a/src/com/owncloud/android/files/services/FileUploader.java b/src/com/owncloud/android/files/services/FileUploader.java
new file mode 100644 (file)
index 0000000..1108462
--- /dev/null
@@ -0,0 +1,924 @@
+/* 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 org.apache.http.HttpStatus;
+import org.apache.jackrabbit.webdav.DavConstants;
+import org.apache.jackrabbit.webdav.MultiStatus;
+import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
+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.db.DbHandler;
+import com.owncloud.android.network.OwnCloudClientUtils;
+import com.owncloud.android.operations.ChunkedUploadFileOperation;
+import com.owncloud.android.operations.CreateFolderOperation;
+import com.owncloud.android.operations.ExistenceCheckOperation;
+import com.owncloud.android.operations.RemoteOperation;
+import com.owncloud.android.operations.RemoteOperationResult;
+import com.owncloud.android.operations.UploadFileOperation;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.ui.activity.FailedUploadActivity;
+import com.owncloud.android.ui.activity.FileActivity;
+import com.owncloud.android.ui.activity.FileDisplayActivity;
+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.OwnCloudVersion;
+
+
+import eu.alefzero.webdav.OnDatatransferProgressListener;
+import eu.alefzero.webdav.WebdavEntry;
+import eu.alefzero.webdav.WebdavUtils;
+
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+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.webkit.MimeTypeMap;
+import android.widget.RemoteViews;
+
+
+import eu.alefzero.webdav.WebdavClient;
+
+public class FileUploader extends Service implements OnDatatransferProgressListener {
+
+    private static final String UPLOAD_FINISH_MESSAGE = "UPLOAD_FINISH";
+    public static final String EXTRA_UPLOAD_RESULT = "RESULT";
+    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 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;
+
+    public static final int UPLOAD_SINGLE_FILE = 0;
+    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;
+    private WebdavClient mUploadClient = null;
+    private Account mLastAccount = null;
+    private FileDataStorageManager mStorageManager;
+
+    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;
+
+    
+    public String getUploadFinishMessage() {
+        return getClass().getName().toString() + UPLOAD_FINISH_MESSAGE;
+    }
+    
+    /**
+     * Builds a key for mPendingUploads from the account and file to upload
+     * 
+     * @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();
+    }
+
+    private String buildRemoteName(Account account, String remotePath) {
+        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.
+     */
+    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);
+        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.
+     */
+    @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_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_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) };
+
+            } 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?
+
+            } else {
+                localPaths = intent.getStringArrayExtra(KEY_LOCAL_FILE);
+                remotePaths = intent.getStringArrayExtra(KEY_REMOTE_FILE);
+                mimeTypes = intent.getStringArrayExtra(KEY_MIME_TYPE);
+            }
+        }
+
+        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);
+        
+        if (intent.hasExtra(KEY_FILE) && files == null) {
+            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_OC.e(TAG, "Incorrect array for local paths provided in upload intent");
+                return Service.START_NOT_STICKY;
+            }
+            if (remotePaths == null) {
+                Log_OC.e(TAG, "Incorrect array for remote paths provided in upload intent");
+                return Service.START_NOT_STICKY;
+            }
+            if (localPaths.length != remotePaths.length) {
+                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);
+                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));
+        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++) {
+                uploadKey = buildRemoteName(account, files[i].getRemotePath());
+                if (chunked) {
+                    newUpload = new ChunkedUploadFileOperation(account, files[i], isInstant, forceOverwrite,
+                            localAction);
+                } else {
+                    newUpload = new UploadFileOperation(account, files[i], isInstant, forceOverwrite, localAction);
+                }
+                if (isInstant) {
+                    newUpload.setRemoteFolderToBeCreated();
+                }
+                mPendingUploads.putIfAbsent(uploadKey, newUpload); // Grants that the file only upload once time
+
+                newUpload.addDatatransferProgressListener(this);
+                newUpload.addDatatransferProgressListener((FileUploaderBinder)mBinder);
+                requestedUploads.add(uploadKey);
+            }
+
+        } catch (IllegalArgumentException e) {
+            Log_OC.e(TAG, "Not enough information provided in intent: " + e.getMessage());
+            return START_NOT_STICKY;
+
+        } catch (IllegalStateException e) {
+            Log_OC.e(TAG, "Bad information provided in intent: " + e.getMessage());
+            return START_NOT_STICKY;
+
+        } catch (Exception 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.
+     * 
+     * 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.
+     * 
+     * It provides by itself the available operations.
+     */
+    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
+         */
+        public void cancel(Account account, OCFile file) {
+            UploadFileOperation upload = null;
+            synchronized (mPendingUploads) {
+                upload = mPendingUploads.remove(buildRemoteName(account, file));
+            }
+            if (upload != null) {
+                upload.cancel();
+            }
+        }
+        
+        
+        
+        public void clearListeners() {
+            mBoundListeners.clear();
+        }
+
+
+        
+        
+        /**
+         * Returns True when the file described by 'file' is being uploaded to
+         * the ownCloud account 'account' or waiting for it
+         * 
+         * If 'file' is a directory, returns 'true' if some of its descendant files is 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
+         */
+        public boolean isUploading(Account account, OCFile file) {
+            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 uploads :(
+                    Iterator<String> it = mPendingUploads.keySet().iterator();
+                    boolean found = false;
+                    while (it.hasNext() && !found) {
+                        found = it.next().startsWith(targetKey);
+                    }
+                    return found;
+                } else {
+                    return (mPendingUploads.containsKey(targetKey));
+                }
+            }
+        }
+
+
+        /**
+         * 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.
+     * 
+     * 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
+        FileUploader mService;
+
+        public ServiceHandler(Looper looper, FileUploader 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> requestedUploads = (AbstractList<String>) msg.obj;
+            if (msg.obj != null) {
+                Iterator<String> it = requestedUploads.iterator();
+                while (it.hasNext()) {
+                    mService.uploadFile(it.next());
+                }
+            }
+            mService.stopSelf(msg.arg1);
+        }
+    }
+
+    /**
+     * Core upload method: sends the file(s) to upload
+     * 
+     * @param uploadKey Key to access the upload to perform, contained in
+     *            mPendingUploads
+     */
+    public void uploadFile(String uploadKey) {
+
+        synchronized (mPendingUploads) {
+            mCurrentUpload = mPendingUploads.get(uploadKey);
+        }
+
+        if (mCurrentUpload != null) {
+
+            notifyUploadStart(mCurrentUpload);
+
+            RemoteOperationResult uploadResult = null, grantResult = null;
+            
+            try {
+                /// prepare client object to send requests to the ownCloud server
+                if (mUploadClient == null || !mLastAccount.equals(mCurrentUpload.getAccount())) {
+                    mLastAccount = mCurrentUpload.getAccount();
+                    mStorageManager = new FileDataStorageManager(mLastAccount, getContentResolver());
+                    mUploadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext());
+                }
+                
+                /// check the existence of the parent folder for the file to upload
+                String remoteParentPath = new File(mCurrentUpload.getRemotePath()).getParent();
+                remoteParentPath = remoteParentPath.endsWith(OCFile.PATH_SEPARATOR) ? remoteParentPath : remoteParentPath + OCFile.PATH_SEPARATOR;
+                grantResult = grantFolderExistence(remoteParentPath);
+            
+                /// perform the upload
+                if (grantResult.isSuccess()) {
+                    OCFile parent = mStorageManager.getFileByPath(remoteParentPath);
+                    mCurrentUpload.getFile().setParentId(parent.getFileId());
+                    uploadResult = mCurrentUpload.execute(mUploadClient);
+                    if (uploadResult.isSuccess()) {
+                        saveUploadedFile();
+                    }
+                } else {
+                    uploadResult = grantResult;
+                }
+                
+            } catch (AccountsException e) {
+                Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
+                uploadResult = new RemoteOperationResult(e);
+                
+            } catch (IOException e) {
+                Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
+                uploadResult = new RemoteOperationResult(e);
+                
+            } finally {
+                synchronized (mPendingUploads) {
+                    mPendingUploads.remove(uploadKey);
+                    Log_OC.i(TAG, "Remove CurrentUploadItem from pending upload Item Map.");
+                }
+                if (uploadResult.isException()) {
+                    // enforce the creation of a new client object for next uploads; this grant that a new socket will 
+                    // be created in the future if the current exception is due to an abrupt lose of network connection
+                    mUploadClient = null;
+                }
+            }
+            
+            /// notify result
+            
+            notifyUploadResult(uploadResult, mCurrentUpload);
+            sendFinalBroadcast(mCurrentUpload, uploadResult);
+
+        }
+
+    }
+
+    /**
+     * Checks the existence of the folder where the current file will be uploaded both in the remote server 
+     * and in the local database.
+     * 
+     * If the upload is set to enforce the creation of the folder, the method tries to create it both remote
+     * and locally.
+     *  
+     *  @param  pathToGrant     Full remote path whose existence will be granted.
+     *  @return  An {@link OCFile} instance corresponding to the folder where the file will be uploaded.
+     */
+    private RemoteOperationResult grantFolderExistence(String pathToGrant) {
+        RemoteOperation operation = new ExistenceCheckOperation(pathToGrant, this, false);
+        RemoteOperationResult result = operation.execute(mUploadClient);
+        if (!result.isSuccess() && result.getCode() == ResultCode.FILE_NOT_FOUND && mCurrentUpload.isRemoteFolderToBeCreated()) {
+            operation = new CreateFolderOperation(  pathToGrant,
+                                                    true,
+                                                    mStorageManager    );
+            result = operation.execute(mUploadClient);
+        }
+        if (result.isSuccess()) {
+            OCFile parentDir = mStorageManager.getFileByPath(pathToGrant);
+            if (parentDir == null) {
+                parentDir = createLocalFolder(pathToGrant);
+            }
+            if (parentDir != null) {
+                result = new RemoteOperationResult(ResultCode.OK);
+            } else {
+                result = new RemoteOperationResult(ResultCode.UNKNOWN_ERROR);
+            }
+        }
+        return result;
+    }
+
+    
+    private OCFile createLocalFolder(String remotePath) {
+        String parentPath = new File(remotePath).getParent();
+        parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR;
+        OCFile parent = mStorageManager.getFileByPath(parentPath);
+        if (parent == null) {
+            parent = createLocalFolder(parentPath);
+        }
+        if (parent != null) {
+            OCFile createdFolder = new OCFile(remotePath);
+            createdFolder.setMimetype("DIR");
+            createdFolder.setParentId(parent.getFileId());
+            mStorageManager.saveFile(createdFolder);
+            return createdFolder;
+        }
+        return null;
+    }
+    
+
+    /**
+     * 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)
+     * 
+     * TODO refactor this ugly thing
+     */
+    private void saveUploadedFile() {
+        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
+        PropFindMethod propfind = null;
+        RemoteOperationResult result = null;
+        try {
+            propfind = new PropFindMethod(mUploadClient.getBaseUri() + WebdavUtils.encodePath(mCurrentUpload.getRemotePath()),
+                    DavConstants.PROPFIND_ALL_PROP,
+                    DavConstants.DEPTH_0);
+            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, propfind.getResponseHeaders());
+            Log_OC.i(TAG, "Update: synchronizing properties for uploaded " + mCurrentUpload.getRemotePath() + ": "
+                    + result.getLogMessage());
+
+        } catch (Exception e) {
+            result = new RemoteOperationResult(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
+        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()
+        }
+
+        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(mCurrentUpload.getEtag());    // TODO Etag, where available
+    }
+
+    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
+
+        // MIME type
+        if (mimeType == null || mimeType.length() <= 0) {
+            try {
+                mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
+                        remotePath.substring(remotePath.lastIndexOf('.') + 1));
+            } catch (IndexOutOfBoundsException e) {
+                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);
+
+        return newFile;
+    }
+
+    /**
+     * Creates a status notification to show the upload progress
+     * 
+     * @param upload Upload operation starting.
+     */
+    @SuppressWarnings("deprecation")
+    private void notifyUploadStart(UploadFileOperation upload) {
+        // / 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.flags |= Notification.FLAG_ONGOING_EVENT;
+        mDefaultNotificationContentView = mNotification.contentView;
+        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.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, FileDisplayActivity.class);
+        showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, upload.getFile());
+        showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, upload.getAccount());
+        showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        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));
+        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);
+            mNotification.contentView.setTextViewText(R.id.status_text, text);
+            mNotificationManager.notify(R.string.uploader_upload_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 an 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
+            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
+            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 = null;
+            if (PreviewImageFragment.canBePreviewed(upload.getFile())) {
+                showDetailsIntent = new Intent(this, PreviewImageActivity.class); 
+            } else {
+                showDetailsIntent = new Intent(this, FileDisplayActivity.class); 
+            }
+            showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, upload.getFile());
+            showDetailsIntent.putExtra(FileActivity.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
+            DbHandler db = new DbHandler(this.getBaseContext());
+            db.removeIUPendingFile(mCurrentUpload.getOriginalStoragePath());
+            db.close();
+
+        } else {
+
+            // / 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());
+            finalNotification.flags |= Notification.FLAG_AUTO_CANCEL;
+            String content = null;
+            
+            boolean needsToUpdateCredentials = (uploadResult.getCode() == ResultCode.UNAUTHORIZED ||
+                    //(uploadResult.isTemporalRedirection() && uploadResult.isIdPRedirection() && 
+                    (uploadResult.isIdPRedirection() &&
+                            MainApp.getAuthTokenTypeSamlSessionCookie().equals(mUploadClient.getAuthTokenType())));
+            if (needsToUpdateCredentials) {
+                // let the user update credentials with one click
+                Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
+                updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, upload.getAccount());
+                updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ENFORCED_UPDATE, true);
+                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);
+                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);
+                mUploadClient = null;   // grant that future retries on the same account will get the fresh credentials
+            } else {
+                // TODO put something smart in the contentIntent below
+            //    finalNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);
+            //}
+            
+                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());
+                }
+
+                // 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);
+        }
+
+    }
+
+    /**
+     * 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
+     */
+    private void sendFinalBroadcast(UploadFileOperation upload, RemoteOperationResult uploadResult) {
+        Intent end = new Intent(getUploadFinishMessage());
+        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());
+        }
+        end.putExtra(EXTRA_OLD_FILE_PATH, upload.getOriginalStoragePath());
+        end.putExtra(ACCOUNT_NAME, upload.getAccount().name);
+        end.putExtra(EXTRA_UPLOAD_RESULT, uploadResult.isSuccess());
+        sendStickyBroadcast(end);
+    }
+
+}
diff --git a/src/com/owncloud/android/files/services/OnUploadCompletedListener.java b/src/com/owncloud/android/files/services/OnUploadCompletedListener.java
new file mode 100644 (file)
index 0000000..b6ee1ac
--- /dev/null
@@ -0,0 +1,26 @@
+/* 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;
+
+public interface OnUploadCompletedListener extends Runnable {
+
+    public boolean getUploadResult();
+
+    public void setUploadResult(boolean result);
+}
diff --git a/src/com/owncloud/android/location/LocationServiceLauncherReciever.java b/src/com/owncloud/android/location/LocationServiceLauncherReciever.java
new file mode 100644 (file)
index 0000000..a974c56
--- /dev/null
@@ -0,0 +1,88 @@
+/* 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.location;
+
+import com.owncloud.android.Log_OC;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningServiceInfo;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+
+public class LocationServiceLauncherReciever extends BroadcastReceiver {
+
+    private final String TAG = getClass().getSimpleName();
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Intent deviceTrackingIntent = new Intent();
+        deviceTrackingIntent
+                .setAction("com.owncloud.android.location.LocationUpdateService");
+        SharedPreferences preferences = PreferenceManager
+                .getDefaultSharedPreferences(context);
+        boolean trackDevice = preferences.getBoolean("enable_devicetracking",
+                true);
+
+        // Used in Preferences activity so that tracking is disabled or
+        // reenabled
+        if (intent.hasExtra("TRACKING_SETTING")) {
+            trackDevice = intent.getBooleanExtra("TRACKING_SETTING", true);
+        }
+
+        startOrStopDeviceTracking(context, trackDevice);
+    }
+
+    /**
+     * Used internally. Starts or stops the device tracking service
+     * 
+     * @param trackDevice true to start the service, false to stop it
+     */
+    private void startOrStopDeviceTracking(Context context, boolean trackDevice) {
+        Intent deviceTrackingIntent = new Intent();
+        deviceTrackingIntent
+                .setAction("com.owncloud.android.location.LocationUpdateService");
+        if (!isDeviceTrackingServiceRunning(context) && trackDevice) {
+            Log_OC.d(TAG, "Starting device tracker service");
+            context.startService(deviceTrackingIntent);
+        } else if (isDeviceTrackingServiceRunning(context) && !trackDevice) {
+            Log_OC.d(TAG, "Stopping device tracker service");
+            context.stopService(deviceTrackingIntent);
+        }
+    }
+
+    /**
+     * Checks to see whether or not the LocationUpdateService is running
+     * 
+     * @return true, if it is. Otherwise false
+     */
+    private boolean isDeviceTrackingServiceRunning(Context context) {
+        ActivityManager manager = (ActivityManager) context
+                .getSystemService(Context.ACTIVITY_SERVICE);
+        for (RunningServiceInfo service : manager
+                .getRunningServices(Integer.MAX_VALUE)) {
+            if (getClass().getName().equals(service.service.getClassName())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/src/com/owncloud/android/location/LocationUpdateService.java b/src/com/owncloud/android/location/LocationUpdateService.java
new file mode 100644 (file)
index 0000000..e1f529b
--- /dev/null
@@ -0,0 +1,112 @@
+/* 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.location;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+
+import android.app.IntentService;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.location.LocationProvider;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.widget.Toast;
+
+
+public class LocationUpdateService extends IntentService implements
+        LocationListener {
+
+    public static final String TAG = "LocationUpdateService";
+
+    private LocationManager mLocationManager;
+    private LocationProvider mLocationProvider;
+    private SharedPreferences mPreferences;
+
+    public LocationUpdateService() {
+        super(TAG);
+    }
+
+    @Override
+    protected void onHandleIntent(Intent intent) {
+        mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
+        // Determine, how we can track the device
+        Criteria criteria = new Criteria();
+        criteria.setAccuracy(Criteria.ACCURACY_FINE);
+        criteria.setPowerRequirement(Criteria.POWER_LOW);
+        mLocationProvider = mLocationManager.getProvider(mLocationManager
+                .getBestProvider(criteria, true));
+
+        // Notify user if there is no way to track the device
+        if (mLocationProvider == null) {
+            String message = String.format(getString(R.string.location_no_provider), getString(R.string.app_name));
+            Toast.makeText(this,
+                    message,
+                    Toast.LENGTH_LONG).show();
+            stopSelf();
+            return;
+        }
+
+        // Get preferences for device tracking
+        mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+        boolean trackDevice = mPreferences.getBoolean("enable_devicetracking",
+                true);
+        int updateIntervall = Integer.parseInt(mPreferences.getString(
+                "devicetracking_update_intervall", "30")) * 60 * 1000;
+        int distanceBetweenLocationChecks = 50;
+
+        // If we do shall track the device -> Stop
+        if (!trackDevice) {
+            Log_OC.d(TAG, "Devicetracking is disabled");
+            stopSelf();
+            return;
+        }
+
+        mLocationManager.requestLocationUpdates(mLocationProvider.getName(),
+                updateIntervall, distanceBetweenLocationChecks, this);
+    }
+
+    @Override
+    public void onLocationChanged(Location location) {
+        Log_OC.d(TAG, "Location changed: " + location);
+
+    }
+
+    @Override
+    public void onProviderDisabled(String arg0) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void onProviderEnabled(String arg0) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
+        // TODO Auto-generated method stub
+
+    }
+
+}
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..b257bd3
--- /dev/null
@@ -0,0 +1,473 @@
+/* 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..eb57222
--- /dev/null
@@ -0,0 +1,706 @@
+/* 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.FileActivity;
+import com.owncloud.android.ui.activity.FileDisplayActivity;
+
+
+/**
+ * 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, FileDisplayActivity.class);
+        showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, mFile);
+        showDetailsIntent.putExtra(FileActivity.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, FileDisplayActivity.class);
+        showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, mFile);
+        showDetailsIntent.putExtra(FileActivity.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);
+    }
+
+}
+
+
diff --git a/src/com/owncloud/android/network/AdvancedSslSocketFactory.java b/src/com/owncloud/android/network/AdvancedSslSocketFactory.java
new file mode 100644 (file)
index 0000000..6a69871
--- /dev/null
@@ -0,0 +1,241 @@
+/* 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.network;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.UnknownHostException;
+import java.security.cert.X509Certificate;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+
+import org.apache.commons.httpclient.ConnectTimeoutException;
+import org.apache.commons.httpclient.params.HttpConnectionParams;
+import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
+import org.apache.http.conn.ssl.X509HostnameVerifier;
+
+import com.owncloud.android.Log_OC;
+
+
+/**
+ * AdvancedSSLProtocolSocketFactory allows to create SSL {@link Socket}s with 
+ * a custom SSLContext and an optional Hostname Verifier.
+ * 
+ * @author David A. Velasco
+ */
+
+public class AdvancedSslSocketFactory implements ProtocolSocketFactory {
+
+    private static final String TAG = AdvancedSslSocketFactory.class.getSimpleName();
+    
+    private SSLContext mSslContext = null;
+    private AdvancedX509TrustManager mTrustManager = null;
+    private X509HostnameVerifier mHostnameVerifier = null;
+
+    public SSLContext getSslContext() {
+        return mSslContext;
+    }
+    
+    /**
+     * Constructor for AdvancedSSLProtocolSocketFactory.
+     */
+    public AdvancedSslSocketFactory(SSLContext sslContext, AdvancedX509TrustManager trustManager, X509HostnameVerifier hostnameVerifier) {
+        if (sslContext == null)
+            throw new IllegalArgumentException("AdvancedSslSocketFactory can not be created with a null SSLContext");
+        if (trustManager == null)
+            throw new IllegalArgumentException("AdvancedSslSocketFactory can not be created with a null Trust Manager");
+        mSslContext = sslContext;
+        mTrustManager = trustManager;
+        mHostnameVerifier = hostnameVerifier;
+    }
+
+    /**
+     * @see ProtocolSocketFactory#createSocket(java.lang.String,int,java.net.InetAddress,int)
+     */
+    public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException, UnknownHostException {
+        Socket socket = mSslContext.getSocketFactory().createSocket(host, port, clientHost, clientPort);
+        verifyPeerIdentity(host, port, socket);
+        return socket;
+    }
+
+    
+    /**
+     * Attempts to get a new socket connection to the given host within the
+     * given time limit.
+     * 
+     * @param host the host name/IP
+     * @param port the port on the host
+     * @param clientHost the local host name/IP to bind the socket to
+     * @param clientPort the port on the local machine
+     * @param params {@link HttpConnectionParams Http connection parameters}
+     * 
+     * @return Socket a new socket
+     * 
+     * @throws IOException if an I/O error occurs while creating the socket
+     * @throws UnknownHostException if the IP address of the host cannot be
+     *             determined
+     */
+    public Socket createSocket(final String host, final int port,
+            final InetAddress localAddress, final int localPort,
+            final HttpConnectionParams params) throws IOException,
+            UnknownHostException, ConnectTimeoutException {
+        Log_OC.d(TAG, "Creating SSL Socket with remote " + host + ":" + port + ", local " + localAddress + ":" + localPort + ", params: " + params);
+        if (params == null) {
+            throw new IllegalArgumentException("Parameters may not be null");
+        } 
+        int timeout = params.getConnectionTimeout();
+        SocketFactory socketfactory = mSslContext.getSocketFactory();
+        Log_OC.d(TAG, " ... with connection timeout " + timeout + " and socket timeout " + params.getSoTimeout());
+        Socket socket = socketfactory.createSocket();
+        SocketAddress localaddr = new InetSocketAddress(localAddress, localPort);
+        SocketAddress remoteaddr = new InetSocketAddress(host, port);
+        socket.setSoTimeout(params.getSoTimeout());
+        socket.bind(localaddr);
+        socket.connect(remoteaddr, timeout);
+        verifyPeerIdentity(host, port, socket);
+        return socket;
+    }
+
+    /**
+     * @see ProtocolSocketFactory#createSocket(java.lang.String,int)
+     */
+    public Socket createSocket(String host, int port) throws IOException,
+            UnknownHostException {
+        Log_OC.d(TAG, "Creating SSL Socket with remote " + host + ":" + port);
+        Socket socket = mSslContext.getSocketFactory().createSocket(host, port);
+        verifyPeerIdentity(host, port, socket);
+        return socket; 
+    }
+
+    public boolean equals(Object obj) {
+        return ((obj != null) && obj.getClass().equals(
+                AdvancedSslSocketFactory.class));
+    }
+
+    public int hashCode() {
+        return AdvancedSslSocketFactory.class.hashCode();
+    }
+
+
+    public X509HostnameVerifier getHostNameVerifier() {
+        return mHostnameVerifier;
+    }
+    
+    
+    public void setHostNameVerifier(X509HostnameVerifier hostnameVerifier) {
+        mHostnameVerifier = hostnameVerifier;
+    }
+    
+    /**
+     * Verifies the identity of the server. 
+     * 
+     * The server certificate is verified first.
+     * 
+     * Then, the host name is compared with the content of the server certificate using the current host name verifier, if any.
+     * @param socket
+     */
+    private void verifyPeerIdentity(String host, int port, Socket socket) throws IOException {
+        try {
+            CertificateCombinedException failInHandshake = null;
+            /// 1. VERIFY THE SERVER CERTIFICATE through the registered TrustManager (that should be an instance of AdvancedX509TrustManager) 
+            try {
+                SSLSocket sock = (SSLSocket) socket;    // a new SSLSession instance is created as a "side effect" 
+                sock.startHandshake();
+                
+            } catch (RuntimeException e) {
+                
+                if (e instanceof CertificateCombinedException) {
+                    failInHandshake = (CertificateCombinedException) e;
+                } else {
+                    Throwable cause = e.getCause();
+                    Throwable previousCause = null;
+                    while (cause != null && cause != previousCause && !(cause instanceof CertificateCombinedException)) {
+                        previousCause = cause;
+                        cause = cause.getCause();
+                    }
+                    if (cause != null && cause instanceof CertificateCombinedException) {
+                        failInHandshake = (CertificateCombinedException)cause;
+                    }
+                }
+                if (failInHandshake == null) {
+                    throw e;
+                }
+                failInHandshake.setHostInUrl(host);
+                
+            }
+            
+            /// 2. VERIFY HOSTNAME
+            SSLSession newSession = null;
+            boolean verifiedHostname = true;
+            if (mHostnameVerifier != null) {
+                if (failInHandshake != null) {
+                    /// 2.1 : a new SSLSession instance was NOT created in the handshake
+                    X509Certificate serverCert = failInHandshake.getServerCertificate();
+                    try {
+                        mHostnameVerifier.verify(host, serverCert);
+                    } catch (SSLException e) {
+                        verifiedHostname = false;
+                    }
+                
+                } else {
+                    /// 2.2 : a new SSLSession instance was created in the handshake
+                    newSession = ((SSLSocket)socket).getSession();
+                    if (!mTrustManager.isKnownServer((X509Certificate)(newSession.getPeerCertificates()[0]))) {
+                        verifiedHostname = mHostnameVerifier.verify(host, newSession); 
+                    }
+                }
+            }
+
+            /// 3. Combine the exceptions to throw, if any
+            if (!verifiedHostname) {
+                SSLPeerUnverifiedException pue = new SSLPeerUnverifiedException("Names in the server certificate do not match to " + host + " in the URL");
+                if (failInHandshake == null) {
+                    failInHandshake = new CertificateCombinedException((X509Certificate) newSession.getPeerCertificates()[0]);
+                    failInHandshake.setHostInUrl(host);
+                }
+                failInHandshake.setSslPeerUnverifiedException(pue);
+                pue.initCause(failInHandshake);
+                throw pue;
+                
+            } else if (failInHandshake != null) {
+                SSLHandshakeException hse = new SSLHandshakeException("Server certificate could not be verified");
+                hse.initCause(failInHandshake);
+                throw hse;
+            }
+            
+        } catch (IOException io) {        
+            try {
+                socket.close();
+            } catch (Exception x) {
+                // NOTHING - irrelevant exception for the caller 
+            }
+            throw io;
+        }
+    }
+
+}
diff --git a/src/com/owncloud/android/network/AdvancedX509TrustManager.java b/src/com/owncloud/android/network/AdvancedX509TrustManager.java
new file mode 100644 (file)
index 0000000..81dcdbd
--- /dev/null
@@ -0,0 +1,147 @@
+/* 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.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertStoreException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+import com.owncloud.android.Log_OC;
+
+
+/**
+ * @author David A. Velasco
+ */
+public class AdvancedX509TrustManager implements X509TrustManager {
+    
+    private static final String TAG = AdvancedX509TrustManager.class.getSimpleName();
+
+    private X509TrustManager mStandardTrustManager = null;
+    private KeyStore mKnownServersKeyStore;
+
+    /**
+     * Constructor for AdvancedX509TrustManager
+     * 
+     * @param  knownServersCertStore    Local certificates store with server certificates explicitly trusted by the user.
+     * @throws CertStoreException       When no default X509TrustManager instance was found in the system.
+     */
+    public AdvancedX509TrustManager(KeyStore knownServersKeyStore)
+            throws NoSuchAlgorithmException, KeyStoreException, CertStoreException {
+        super();
+        TrustManagerFactory factory = TrustManagerFactory
+                .getInstance(TrustManagerFactory.getDefaultAlgorithm());
+        factory.init((KeyStore)null);
+        mStandardTrustManager = findX509TrustManager(factory);
+
+        mKnownServersKeyStore = knownServersKeyStore;
+    }
+    
+    
+    /**
+     * Locates the first X509TrustManager provided by a given TrustManagerFactory
+     * @param factory               TrustManagerFactory to inspect in the search for a X509TrustManager
+     * @return                      The first X509TrustManager found in factory.
+     * @throws CertStoreException   When no X509TrustManager instance was found in factory
+     */
+    private X509TrustManager findX509TrustManager(TrustManagerFactory factory) throws CertStoreException {
+        TrustManager tms[] = factory.getTrustManagers();
+        for (int i = 0; i < tms.length; i++) {
+            if (tms[i] instanceof X509TrustManager) {
+                return (X509TrustManager) tms[i];
+            }
+        }
+        return null;
+    }
+    
+
+    /**
+     * @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[],
+     *      String authType)
+     */
+    public void checkClientTrusted(X509Certificate[] certificates, String authType) throws CertificateException {
+        mStandardTrustManager.checkClientTrusted(certificates, authType);
+    }
+
+    
+    /**
+     * @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[],
+     *      String authType)
+     */
+    public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException {
+        if (!isKnownServer(certificates[0])) {
+               CertificateCombinedException result = new CertificateCombinedException(certificates[0]);
+               try {
+                       certificates[0].checkValidity();
+               } catch (CertificateExpiredException c) {
+                       result.setCertificateExpiredException(c);
+                       
+               } catch (CertificateNotYetValidException c) {
+                result.setCertificateNotYetException(c);
+               }
+               
+               try {
+                   mStandardTrustManager.checkServerTrusted(certificates, authType);
+               } catch (CertificateException c) {
+                Throwable cause = c.getCause();
+                Throwable previousCause = null;
+                while (cause != null && cause != previousCause && !(cause instanceof CertPathValidatorException)) {     // getCause() is not funny
+                    previousCause = cause;
+                    cause = cause.getCause();
+                }
+                if (cause != null && cause instanceof CertPathValidatorException) {
+                       result.setCertPathValidatorException((CertPathValidatorException)cause);
+                } else {
+                       result.setOtherCertificateException(c);
+                }
+               }
+               
+               if (result.isException())
+                       throw result;
+
+        }
+    }
+    
+    
+    /**
+     * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()
+     */
+    public X509Certificate[] getAcceptedIssuers() {
+        return mStandardTrustManager.getAcceptedIssuers();
+    }
+
+    
+    public boolean isKnownServer(X509Certificate cert) {
+        try {
+            return (mKnownServersKeyStore.getCertificateAlias(cert) != null);
+        } catch (KeyStoreException e) {
+            Log_OC.d(TAG, "Fail while checking certificate in the known-servers store");
+            return false;
+        }
+    }
+    
+}
\ No newline at end of file
diff --git a/src/com/owncloud/android/network/BearerAuthScheme.java b/src/com/owncloud/android/network/BearerAuthScheme.java
new file mode 100644 (file)
index 0000000..16791c2
--- /dev/null
@@ -0,0 +1,269 @@
+/* ownCloud Android client application
+ *   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 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.Map;
+
+import org.apache.commons.httpclient.Credentials;
+import org.apache.commons.httpclient.HttpMethod;
+import org.apache.commons.httpclient.auth.AuthChallengeParser;
+import org.apache.commons.httpclient.auth.AuthScheme;
+import org.apache.commons.httpclient.auth.AuthenticationException;
+import org.apache.commons.httpclient.auth.InvalidCredentialsException;
+import org.apache.commons.httpclient.auth.MalformedChallengeException;
+
+import com.owncloud.android.Log_OC;
+
+
+/**
+ * Bearer authentication scheme as defined in RFC 6750.
+ * 
+ * @author David A. Velasco
+ */
+
+public class BearerAuthScheme implements AuthScheme /*extends RFC2617Scheme*/ {
+    
+    private static final String TAG = BearerAuthScheme.class.getSimpleName();
+
+    public static final String AUTH_POLICY = "Bearer";
+    
+    /** Whether the bearer authentication process is complete */
+    private boolean mComplete;
+    
+    /** Authentication parameter map */
+    private Map mParams = null;
+    
+    
+    /**
+     * Default constructor for the bearer authentication scheme.
+     */
+    public BearerAuthScheme() {
+        mComplete = false;
+    }
+
+    /**
+     * Constructor for the basic authentication scheme.
+     * 
+     * @param   challenge                       Authentication challenge
+     * 
+     * @throws  MalformedChallengeException     Thrown if the authentication challenge is malformed
+     * 
+     * @deprecated Use parameterless constructor and {@link AuthScheme#processChallenge(String)} method
+     */
+    public BearerAuthScheme(final String challenge) throws MalformedChallengeException {
+        processChallenge(challenge);
+        mComplete = true;
+    }
+
+    /**
+     * Returns textual designation of the bearer authentication scheme.
+     * 
+     * @return "Bearer"
+     */
+    public String getSchemeName() {
+        return "bearer";
+    }
+
+    /**
+     * Processes the Bearer challenge.
+     *  
+     * @param   challenge                   The challenge string
+     * 
+     * @throws MalformedChallengeException  Thrown if the authentication challenge is malformed
+     */
+    public void processChallenge(String challenge) throws MalformedChallengeException {
+        String s = AuthChallengeParser.extractScheme(challenge);
+        if (!s.equalsIgnoreCase(getSchemeName())) {
+            throw new MalformedChallengeException(
+              "Invalid " + getSchemeName() + " challenge: " + challenge); 
+        }
+        mParams = AuthChallengeParser.extractParams(challenge);
+        mComplete = true;
+    }
+
+    /**
+     * Tests if the Bearer authentication process has been completed.
+     * 
+     * @return 'true' if Bearer authorization has been processed, 'false' otherwise.
+     */
+    public boolean isComplete() {
+        return this.mComplete;
+    }
+
+    /**
+     * Produces bearer authorization string for the given set of 
+     * {@link Credentials}.
+     * 
+     * @param   credentials                     The set of credentials to be used for authentication
+     * @param   method                          Method name is ignored by the bearer authentication scheme
+     * @param   uri                             URI is ignored by the bearer authentication scheme
+     * @throws  InvalidCredentialsException     If authentication credentials are not valid or not applicable 
+     *                                          for this authentication scheme
+     * @throws  AuthenticationException         If authorization string cannot be generated due to an authentication failure
+     * @return  A bearer authorization string
+     * 
+     * @deprecated Use {@link #authenticate(Credentials, HttpMethod)}
+     */
+    public String authenticate(Credentials credentials, String method, String uri) throws AuthenticationException {
+        Log_OC.d(TAG, "enter BearerScheme.authenticate(Credentials, String, String)");
+
+        BearerCredentials bearer = null;
+        try {
+            bearer = (BearerCredentials) credentials;
+        } catch (ClassCastException e) {
+            throw new InvalidCredentialsException(
+             "Credentials cannot be used for bearer authentication: " 
+              + credentials.getClass().getName());
+        }
+        return BearerAuthScheme.authenticate(bearer);
+    }
+
+    
+    /**
+     * Returns 'false'. Bearer authentication scheme is request based.
+     * 
+     * @return 'false'.
+     */
+    public boolean isConnectionBased() {
+        return false;    
+    }
+
+    /**
+     * Produces bearer authorization string for the given set of {@link Credentials}.
+     * 
+     * @param   credentials                     The set of credentials to be used for authentication
+     * @param   method                          The method being authenticated
+     * @throws  InvalidCredentialsException     If authentication credentials are not valid or not applicable for this authentication 
+     *                                          scheme.
+     * @throws AuthenticationException         If authorization string cannot be generated due to an authentication failure.
+     * 
+     * @return a basic authorization string
+     */
+    public String authenticate(Credentials credentials, HttpMethod method) throws AuthenticationException {
+        Log_OC.d(TAG, "enter BearerScheme.authenticate(Credentials, HttpMethod)");
+
+        if (method == null) {
+            throw new IllegalArgumentException("Method may not be null");
+        }
+        BearerCredentials bearer = null;
+        try {
+            bearer = (BearerCredentials) credentials;
+        } catch (ClassCastException e) {
+            throw new InvalidCredentialsException(
+                    "Credentials cannot be used for bearer authentication: " 
+                    + credentials.getClass().getName());
+        }
+        return BearerAuthScheme.authenticate(
+            bearer, 
+            method.getParams().getCredentialCharset());
+    }
+    
+    /**
+     * @deprecated Use {@link #authenticate(BearerCredentials, String)}
+     * 
+     * Returns a bearer Authorization header value for the given 
+     * {@link BearerCredentials}.
+     * 
+     * @param   credentials     The credentials to encode.
+     * 
+     * @return                  A bearer authorization string
+     */
+    public static String authenticate(BearerCredentials credentials) {
+        return authenticate(credentials, "ISO-8859-1");
+    }
+
+    /**
+     * Returns a bearer Authorization header value for the given 
+     * {@link BearerCredentials} and charset.
+     * 
+     * @param   credentials         The credentials to encode.
+     * @param   charset             The charset to use for encoding the credentials
+     * 
+     * @return                      A bearer authorization string
+     * 
+     * @since 3.0
+     */
+    public static String authenticate(BearerCredentials credentials, String charset) {
+        Log_OC.d(TAG, "enter BearerAuthScheme.authenticate(BearerCredentials, String)");
+
+        if (credentials == null) {
+            throw new IllegalArgumentException("Credentials may not be null"); 
+        }
+        if (charset == null || charset.length() == 0) {
+            throw new IllegalArgumentException("charset may not be null or empty");
+        }
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(credentials.getAccessToken());
+        
+        //return "Bearer " + EncodingUtil.getAsciiString(EncodingUtil.getBytes(buffer.toString(), charset));
+        return "Bearer " + buffer.toString();
+    }
+
+    /**
+     * Returns a String identifying the authentication challenge.  This is
+     * used, in combination with the host and port to determine if
+     * authorization has already been attempted or not.  Schemes which
+     * require multiple requests to complete the authentication should
+     * return a different value for each stage in the request.
+     * 
+     * Additionally, the ID should take into account any changes to the
+     * authentication challenge and return a different value when appropriate.
+     * For example when the realm changes in basic authentication it should be
+     * considered a different authentication attempt and a different value should
+     * be returned.
+     * 
+     * This method simply returns the realm for the challenge.
+     * 
+     * @return String       a String identifying the authentication challenge.
+     * 
+     * @deprecated no longer used
+     */
+    @Override
+    public String getID() {
+        return getRealm();
+    }
+
+    /**
+     * Returns authentication parameter with the given name, if available.
+     * 
+     * @param   name    The name of the parameter to be returned
+     * 
+     * @return          The parameter with the given name
+     */
+    @Override
+    public String getParameter(String name) {
+        if (name == null) {
+            throw new IllegalArgumentException("Parameter name may not be null"); 
+        }
+        if (mParams == null) {
+            return null;
+        }
+        return (String) mParams.get(name.toLowerCase());
+    }
+
+    /**
+     * Returns authentication realm. The realm may not be null.
+     * 
+     * @return  The authentication realm
+     */
+    @Override
+    public String getRealm() {
+        return getParameter("realm");
+    }
+    
+}
diff --git a/src/com/owncloud/android/network/BearerCredentials.java b/src/com/owncloud/android/network/BearerCredentials.java
new file mode 100644 (file)
index 0000000..50799b0
--- /dev/null
@@ -0,0 +1,97 @@
+/* ownCloud Android client application
+ *   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 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 org.apache.commons.httpclient.Credentials;
+import org.apache.commons.httpclient.util.LangUtils;
+
+/**
+ * Bearer token {@link Credentials}
+ *
+ * @author David A. Velasco
+ */
+public class BearerCredentials implements Credentials {
+
+    
+    private String mAccessToken;
+    
+    
+    /**
+     * The constructor with the bearer token
+     *
+     * @param token     The bearer token
+     */
+    public BearerCredentials(String token) {
+        /*if (token == null) {
+            throw new IllegalArgumentException("Bearer token may not be null");            
+        }*/
+        mAccessToken = (token == null) ? "" : token;
+    }
+
+
+    /**
+     * Returns the access token
+     *
+     * @return      The access token
+     */
+    public String getAccessToken() {
+        return mAccessToken;
+    }
+
+
+    /**
+     * Get this object string.
+     *
+     * @return  The access token
+     */
+    public String toString() {
+        return mAccessToken;
+    }
+
+    /**
+     * Does a hash of the access token.
+     *
+     * @return The hash code of the access token
+     */
+    public int hashCode() {
+        int hash = LangUtils.HASH_SEED;
+        hash = LangUtils.hashCode(hash, mAccessToken);
+        return hash;
+    }
+
+    /**
+     * These credentials are assumed equal if accessToken is the same.
+     *
+     * @param   o   The other object to compare with.
+     *
+     * @return      'True' if the object is equivalent.
+     */
+    public boolean equals(Object o) {
+        if (o == null) return false;
+        if (this == o) return true;
+        if (this.getClass().equals(o.getClass())) {
+            BearerCredentials that = (BearerCredentials) o;
+            if (LangUtils.equals(mAccessToken, that.mAccessToken)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+}
+
diff --git a/src/com/owncloud/android/network/CertificateCombinedException.java b/src/com/owncloud/android/network/CertificateCombinedException.java
new file mode 100644 (file)
index 0000000..e96d9dc
--- /dev/null
@@ -0,0 +1,130 @@
+/* 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.security.cert.CertPathValidatorException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.SSLPeerUnverifiedException;
+
+/**
+ * Exception joining all the problems that {@link AdvancedX509TrustManager} can find in
+ * a certificate chain for a server.
+ * 
+ * This was initially created as an extension of CertificateException, but some
+ * implementations of the SSL socket layer in existing devices are REPLACING the CertificateException
+ * instances thrown by {@link javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[], String)}
+ * with SSLPeerUnverifiedException FORGETTING THE CAUSING EXCEPTION instead of wrapping it. 
+ * 
+ * Due to this, extending RuntimeException is necessary to get that the CertificateCombinedException 
+ * instance reaches {@link AdvancedSslSocketFactory#verifyPeerIdentity}.
+ * 
+ * BE CAREFUL. As a RuntimeException extensions, Java compilers do not require to handle it
+ * in client methods. Be sure to use it only when you know exactly where it will go.
+ * 
+ * @author David A. Velasco
+ */
+public class CertificateCombinedException extends RuntimeException {
+
+    /** Generated - to refresh every time the class changes */
+    private static final long serialVersionUID = -8875782030758554999L;
+    
+    private X509Certificate mServerCert = null;
+    private String mHostInUrl;
+
+    private CertificateExpiredException mCertificateExpiredException = null;
+    private CertificateNotYetValidException mCertificateNotYetValidException = null;
+    private CertPathValidatorException mCertPathValidatorException = null;
+    private CertificateException mOtherCertificateException = null;
+    private SSLPeerUnverifiedException mSslPeerUnverifiedException = null;
+    
+    public CertificateCombinedException(X509Certificate x509Certificate) {
+        mServerCert = x509Certificate;
+    }
+
+    public X509Certificate getServerCertificate() {
+        return mServerCert;
+    }
+
+    public String getHostInUrl() {
+        return mHostInUrl;
+    }
+
+    public void setHostInUrl(String host) {
+        mHostInUrl = host;
+    }
+
+    public CertificateExpiredException getCertificateExpiredException() {
+        return mCertificateExpiredException;
+    }
+
+    public void setCertificateExpiredException(CertificateExpiredException c) {
+        mCertificateExpiredException  = c;
+    }
+
+    public CertificateNotYetValidException getCertificateNotYetValidException() {
+        return mCertificateNotYetValidException;
+    }
+
+    public void setCertificateNotYetException(CertificateNotYetValidException c) {
+        mCertificateNotYetValidException = c;
+    }
+
+    public CertPathValidatorException getCertPathValidatorException() {
+        return mCertPathValidatorException;
+    }
+
+    public void setCertPathValidatorException(CertPathValidatorException c) {
+        mCertPathValidatorException = c;
+    }
+
+    public CertificateException getOtherCertificateException() {
+        return mOtherCertificateException;
+    }
+
+    public void setOtherCertificateException(CertificateException c) {
+        mOtherCertificateException = c;
+    }
+
+    public SSLPeerUnverifiedException getSslPeerUnverifiedException() {
+        return mSslPeerUnverifiedException ; 
+    }
+
+    public void setSslPeerUnverifiedException(SSLPeerUnverifiedException s) {
+        mSslPeerUnverifiedException = s;
+    }
+
+    public boolean isException() {
+        return (mCertificateExpiredException != null ||
+                mCertificateNotYetValidException != null ||
+                mCertPathValidatorException != null ||
+                mOtherCertificateException != null ||
+                mSslPeerUnverifiedException != null);
+    }
+
+    public boolean isRecoverable() {
+        return (mCertificateExpiredException != null ||
+                mCertificateNotYetValidException != null ||
+                mCertPathValidatorException != null ||
+                mSslPeerUnverifiedException != null);
+    }
+
+}
diff --git a/src/com/owncloud/android/network/OwnCloudClientUtils.java b/src/com/owncloud/android/network/OwnCloudClientUtils.java
new file mode 100644 (file)
index 0000000..bb29938
--- /dev/null
@@ -0,0 +1,285 @@
+/* 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.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+
+import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
+import org.apache.commons.httpclient.protocol.Protocol;
+import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
+import org.apache.http.conn.ssl.X509HostnameVerifier;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.authentication.AccountAuthenticator;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.authentication.AccountUtils.AccountNotFoundException;
+
+
+import eu.alefzero.webdav.WebdavClient;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.app.Activity;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+
+public class OwnCloudClientUtils {
+    
+    final private static String TAG = OwnCloudClientUtils.class.getSimpleName();
+    
+    /** Default timeout for waiting data from the server */
+    public static final int DEFAULT_DATA_TIMEOUT = 60000;
+    
+    /** Default timeout for establishing a connection */
+    public static final int DEFAULT_CONNECTION_TIMEOUT = 60000;
+
+    /** Connection manager for all the WebdavClients */
+    private static MultiThreadedHttpConnectionManager mConnManager = null;
+    
+    private static Protocol mDefaultHttpsProtocol = null;
+
+    private static AdvancedSslSocketFactory mAdvancedSslSocketFactory = null;
+
+    private static X509HostnameVerifier mHostnameVerifier = null;
+    
+    
+    /**
+     * Creates a WebdavClient setup for an ownCloud account
+     * 
+     * Do not call this method from the main thread.
+     * 
+     * @param account                       The ownCloud account
+     * @param appContext                    Android application context
+     * @return                              A WebdavClient object ready to be used
+     * @throws AuthenticatorException       If the authenticator failed to get the authorization token for the account.
+     * @throws OperationCanceledException   If the authenticator operation was cancelled while getting the authorization token for the account. 
+     * @throws IOException                  If there was some I/O error while getting the authorization token for the account.
+     * @throws AccountNotFoundException     If 'account' is unknown for the AccountManager
+     */
+    public static WebdavClient createOwnCloudClient (Account account, Context appContext) throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException {
+        //Log_OC.d(TAG, "Creating WebdavClient associated to " + account.name);
+       
+        Uri uri = Uri.parse(AccountUtils.constructFullURLForAccount(appContext, account));
+        AccountManager am = AccountManager.get(appContext);
+        boolean isOauth2 = am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null;   // TODO avoid calling to getUserData here
+        boolean isSamlSso = am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO) != null;
+        WebdavClient client = createOwnCloudClient(uri, appContext, !isSamlSso);
+        if (isOauth2) {    
+            String accessToken = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypeAccessToken(), false);
+            client.setBearerCredentials(accessToken);   // TODO not assume that the access token is a bearer token
+        
+        } else if (isSamlSso) {    // TODO avoid a call to getUserData here
+            String accessToken = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypeSamlSessionCookie(), false);
+            client.setSsoSessionCookie(accessToken);
+            
+        } else {
+            String username = account.name.substring(0, account.name.lastIndexOf('@'));
+            //String password = am.getPassword(account);
+            String password = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypePass(), false);
+            client.setBasicCredentials(username, password);
+        }
+        
+        return client;
+    }
+    
+    
+    public static WebdavClient createOwnCloudClient (Account account, Context appContext, Activity currentActivity) throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException {
+        Uri uri = Uri.parse(AccountUtils.constructFullURLForAccount(appContext, account));
+        AccountManager am = AccountManager.get(appContext);
+        boolean isOauth2 = am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null;   // TODO avoid calling to getUserData here
+        boolean isSamlSso = am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO) != null;
+        WebdavClient client = createOwnCloudClient(uri, appContext, !isSamlSso);
+        
+        if (isOauth2) {    // TODO avoid a call to getUserData here
+            AccountManagerFuture<Bundle> future =  am.getAuthToken(account, MainApp.getAuthTokenTypeAccessToken(), null, currentActivity, null, null);
+            Bundle result = future.getResult();
+            String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN);
+            if (accessToken == null) throw new AuthenticatorException("WTF!");
+            client.setBearerCredentials(accessToken);   // TODO not assume that the access token is a bearer token
+
+        } else if (isSamlSso) {    // TODO avoid a call to getUserData here
+            AccountManagerFuture<Bundle> future =  am.getAuthToken(account, MainApp.getAuthTokenTypeSamlSessionCookie(), null, currentActivity, null, null);
+            Bundle result = future.getResult();
+            String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN);
+            if (accessToken == null) throw new AuthenticatorException("WTF!");
+            client.setSsoSessionCookie(accessToken);
+
+        } else {
+            String username = account.name.substring(0, account.name.lastIndexOf('@'));
+            //String password = am.getPassword(account);
+            //String password = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypePass(), false);
+            AccountManagerFuture<Bundle> future =  am.getAuthToken(account, MainApp.getAuthTokenTypePass(), null, currentActivity, null, null);
+            Bundle result = future.getResult();
+            String password = result.getString(AccountManager.KEY_AUTHTOKEN);
+            client.setBasicCredentials(username, password);
+        }
+        
+        return client;
+    }
+    
+    /**
+     * Creates a WebdavClient to access a URL and sets the desired parameters for ownCloud client connections.
+     * 
+     * @param uri       URL to the ownCloud server
+     * @param context   Android context where the WebdavClient is being created.
+     * @return          A WebdavClient object ready to be used
+     */
+    public static WebdavClient createOwnCloudClient(Uri uri, Context context, boolean followRedirects) {
+        try {
+            registerAdvancedSslContext(true, context);
+        }  catch (GeneralSecurityException 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_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());
+        
+        client.setDefaultTimeouts(DEFAULT_DATA_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT);
+        client.setBaseUri(uri);
+        client.setFollowRedirects(followRedirects);
+        
+        return client;
+    }
+    
+    
+    /**
+     * Registers or unregisters the proper components for advanced SSL handling.
+     * @throws IOException 
+     */
+    private static void registerAdvancedSslContext(boolean register, Context context) throws GeneralSecurityException, IOException {
+        Protocol pr = null;
+        try {
+            pr = Protocol.getProtocol("https");
+            if (pr != null && mDefaultHttpsProtocol == null) {
+                mDefaultHttpsProtocol = pr;
+            }
+        } catch (IllegalStateException e) {
+            // nothing to do here; really
+        }
+        boolean isRegistered = (pr != null && pr.getSocketFactory() instanceof AdvancedSslSocketFactory);
+        if (register && !isRegistered) {
+            Protocol.registerProtocol("https", new Protocol("https", getAdvancedSslSocketFactory(context), 443));
+            
+        } else if (!register && isRegistered) {
+            if (mDefaultHttpsProtocol != null) {
+                Protocol.registerProtocol("https", mDefaultHttpsProtocol);
+            }
+        }
+    }
+    
+    public static AdvancedSslSocketFactory getAdvancedSslSocketFactory(Context context) throws GeneralSecurityException, IOException {
+        if (mAdvancedSslSocketFactory  == null) {
+            KeyStore trustStore = getKnownServersStore(context);
+            AdvancedX509TrustManager trustMgr = new AdvancedX509TrustManager(trustStore);
+            TrustManager[] tms = new TrustManager[] { trustMgr };
+                
+            SSLContext sslContext = SSLContext.getInstance("TLS");
+            sslContext.init(null, tms, null);
+                    
+            mHostnameVerifier = new BrowserCompatHostnameVerifier();
+            mAdvancedSslSocketFactory = new AdvancedSslSocketFactory(sslContext, trustMgr, mHostnameVerifier);
+        }
+        return mAdvancedSslSocketFactory;
+    }
+
+
+    private static String LOCAL_TRUSTSTORE_FILENAME = "knownServers.bks";
+    
+    private static String LOCAL_TRUSTSTORE_PASSWORD = "password";
+
+    private static KeyStore mKnownServersStore = null;
+    
+    /**
+     * Returns the local store of reliable server certificates, explicitly accepted by the user.
+     * 
+     * Returns a KeyStore instance with empty content if the local store was never created.
+     * 
+     * Loads the store from the storage environment if needed.
+     * 
+     * @param context                       Android context where the operation is being performed.
+     * @return                              KeyStore instance with explicitly-accepted server certificates. 
+     * @throws KeyStoreException            When the KeyStore instance could not be created.
+     * @throws IOException                  When an existing local trust store could not be loaded.
+     * @throws NoSuchAlgorithmException     When the existing local trust store was saved with an unsupported algorithm.
+     * @throws CertificateException         When an exception occurred while loading the certificates from the local trust store.
+     */
+    private static KeyStore getKnownServersStore(Context context) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
+        if (mKnownServersStore == null) {
+            //mKnownServersStore = KeyStore.getInstance("BKS");
+            mKnownServersStore = KeyStore.getInstance(KeyStore.getDefaultType());
+            File localTrustStoreFile = new File(context.getFilesDir(), LOCAL_TRUSTSTORE_FILENAME);
+            Log_OC.d(TAG, "Searching known-servers store at " + localTrustStoreFile.getAbsolutePath());
+            if (localTrustStoreFile.exists()) {
+                InputStream in = new FileInputStream(localTrustStoreFile);
+                try {
+                    mKnownServersStore.load(in, LOCAL_TRUSTSTORE_PASSWORD.toCharArray());
+                } finally {
+                    in.close();
+                }
+            } else {
+                mKnownServersStore.load(null, LOCAL_TRUSTSTORE_PASSWORD.toCharArray()); // necessary to initialize an empty KeyStore instance
+            }
+        }
+        return mKnownServersStore;
+    }
+    
+    
+    public static void addCertToKnownServersStore(Certificate cert, Context context) throws  KeyStoreException, NoSuchAlgorithmException, 
+                                                                                            CertificateException, IOException {
+        KeyStore knownServers = getKnownServersStore(context);
+        knownServers.setCertificateEntry(Integer.toString(cert.hashCode()), cert);
+        FileOutputStream fos = null;
+        try {
+            fos = context.openFileOutput(LOCAL_TRUSTSTORE_FILENAME, Context.MODE_PRIVATE);
+            knownServers.store(fos, LOCAL_TRUSTSTORE_PASSWORD.toCharArray());
+        } finally {
+            fos.close();
+        }
+    }
+    
+    
+    static private MultiThreadedHttpConnectionManager getMultiThreadedConnManager() {
+        if (mConnManager == null) {
+            mConnManager = new MultiThreadedHttpConnectionManager();
+            mConnManager.getParams().setDefaultMaxConnectionsPerHost(5);
+            mConnManager.getParams().setMaxTotalConnections(5);
+        }
+        return mConnManager;
+    }
+
+
+}
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);
+
+}
diff --git a/src/com/owncloud/android/operations/ChunkedUploadFileOperation.java b/src/com/owncloud/android/operations/ChunkedUploadFileOperation.java
new file mode 100644 (file)
index 0000000..2649d19
--- /dev/null
@@ -0,0 +1,97 @@
+/* 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.operations;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.channels.FileChannel;
+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 eu.alefzero.webdav.ChunkFromFileChannelRequestEntity;
+import eu.alefzero.webdav.WebdavClient;
+import eu.alefzero.webdav.WebdavUtils;
+
+public class ChunkedUploadFileOperation extends UploadFileOperation {
+    
+    private static final long CHUNK_SIZE = 1024000;
+    private static final String OC_CHUNKED_HEADER = "OC-Chunked";
+    private static final String TAG = ChunkedUploadFileOperation.class.getSimpleName();
+
+    public ChunkedUploadFileOperation(  Account account,
+                                        OCFile file,
+                                        boolean isInstant, 
+                                        boolean forceOverwrite,
+                                        int localBehaviour) {
+        
+        super(account, file, isInstant, forceOverwrite, localBehaviour);
+    }
+
+    @Override
+    protected int uploadFile(WebdavClient client) throws HttpException, IOException {
+        int status = -1;
+
+        FileChannel channel = null;
+        RandomAccessFile raf = null;
+        try {
+            File file = new File(getStoragePath());
+            raf = new RandomAccessFile(file, "r");
+            channel = raf.getChannel();
+            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) {
+                if (mPutMethod != null) {
+                    mPutMethod.releaseConnection();    // let the connection available for other methods
+                }
+                mPutMethod = new PutMethod(uriPrefix + chunkCount + "-" + chunkIndex);
+                mPutMethod.addRequestHeader(OC_CHUNKED_HEADER, OC_CHUNKED_HEADER);
+                ((ChunkFromFileChannelRequestEntity)mEntity).setOffset(offset);
+                mPutMethod.setRequestEntity(mEntity);
+                status = client.executeMethod(mPutMethod);
+                client.exhaustResponse(mPutMethod.getResponseBodyAsStream());
+                Log_OC.d(TAG, "Upload of " + getStoragePath() + " to " + getRemotePath() + ", chunk index " + chunkIndex + ", count " + chunkCount + ", HTTP result status " + status);
+                if (!isSuccess(status))
+                    break;
+            }
+            
+        } finally {
+            if (channel != null)
+                channel.close();
+            if (raf != null)
+                raf.close();
+            if (mPutMethod != null)
+                mPutMethod.releaseConnection();    // let the connection available for other methods
+        }
+        return status;
+    }
+
+}
diff --git a/src/com/owncloud/android/operations/CreateFolderOperation.java b/src/com/owncloud/android/operations/CreateFolderOperation.java
new file mode 100644 (file)
index 0000000..b3456ee
--- /dev/null
@@ -0,0 +1,118 @@
+/* ownCloud Android client application
+ *   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 version 2,
+ *   as published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package com.owncloud.android.operations;
+
+import java.io.File;
+
+import org.apache.commons.httpclient.HttpStatus;
+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 eu.alefzero.webdav.WebdavClient;
+import eu.alefzero.webdav.WebdavUtils;
+
+/**
+ * Remote operation performing the creation of a new folder in the ownCloud server.
+ * 
+ * @author David A. Velasco 
+ */
+public class CreateFolderOperation extends RemoteOperation {
+    
+    private static final String TAG = CreateFolderOperation.class.getSimpleName();
+
+    private static final int READ_TIMEOUT = 10000;
+    private static final int CONNECTION_TIMEOUT = 5000;
+    
+    protected String mRemotePath;
+    protected boolean mCreateFullPath;
+    protected DataStorageManager mStorageManager;
+    
+    /**
+     * Constructor
+     * 
+     * @param remotePath            Full path to the new directory to create in the remote server.
+     * @param createFullPath        'True' means that all the ancestor folders should be created if don't exist yet.
+     * @param storageManager        Reference to the local database corresponding to the account where the file is contained. 
+     */
+    public CreateFolderOperation(String remotePath, boolean createFullPath, DataStorageManager storageManager) {
+        mRemotePath = remotePath;
+        mCreateFullPath = createFullPath;
+        mStorageManager = storageManager;
+    }
+    
+    
+    /**
+     * Performs the operation
+     * 
+     * @param   client      Client object to communicate with the remote ownCloud server.
+     */
+    @Override
+    protected RemoteOperationResult run(WebdavClient client) {
+        RemoteOperationResult result = null;
+        MkColMethod mkcol = null;
+        try {
+            mkcol = new MkColMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath));
+            int status =  client.executeMethod(mkcol, READ_TIMEOUT, CONNECTION_TIMEOUT);
+            if (!mkcol.succeeded() && mkcol.getStatusCode() == HttpStatus.SC_CONFLICT && mCreateFullPath) {
+                result = createParentFolder(getParentPath(), client);
+                status = client.executeMethod(mkcol, READ_TIMEOUT, CONNECTION_TIMEOUT);    // second (and last) try
+            }
+            if (mkcol.succeeded()) {
+                // Save new directory in local database
+                OCFile newDir = new OCFile(mRemotePath);
+                newDir.setMimetype("DIR");
+                long parentId = mStorageManager.getFileByPath(getParentPath()).getFileId();
+                newDir.setParentId(parentId);
+                newDir.setModificationTimestamp(System.currentTimeMillis());
+                mStorageManager.saveFile(newDir);
+            }
+            result = new RemoteOperationResult(mkcol.succeeded(), status, mkcol.getResponseHeaders());
+            Log_OC.d(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage());
+            client.exhaustResponse(mkcol.getResponseBodyAsStream());
+                
+        } catch (Exception e) {
+            result = new RemoteOperationResult(e);
+            Log_OC.e(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage(), e);
+            
+        } finally {
+            if (mkcol != null)
+                mkcol.releaseConnection();
+        }
+        return result;
+    }
+
+
+    private String getParentPath() {
+        String parentPath = new File(mRemotePath).getParent();
+        parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR;
+        return parentPath;
+    }
+
+    
+    private RemoteOperationResult createParentFolder(String parentPath, WebdavClient client) {
+        RemoteOperation operation = new CreateFolderOperation(  parentPath,
+                                                                mCreateFullPath,
+                                                                mStorageManager    );
+        return operation.execute(client);
+    }
+
+}
diff --git a/src/com/owncloud/android/operations/DownloadFileOperation.java b/src/com/owncloud/android/operations/DownloadFileOperation.java
new file mode 100644 (file)
index 0000000..db986f4
--- /dev/null
@@ -0,0 +1,236 @@
+/* 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.operations;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.commons.httpclient.Header;
+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;
+import com.owncloud.android.utils.FileStorageUtils;
+
+
+import eu.alefzero.webdav.OnDatatransferProgressListener;
+import eu.alefzero.webdav.WebdavClient;
+import eu.alefzero.webdav.WebdavUtils;
+import android.accounts.Account;
+import android.webkit.MimeTypeMap;
+
+/**
+ * Remote operation performing the download of a file to an ownCloud server
+ * 
+ * @author David A. Velasco
+ */
+public class DownloadFileOperation extends RemoteOperation {
+    
+    private static final String TAG = DownloadFileOperation.class.getSimpleName();
+
+    private Account mAccount;
+    private OCFile mFile;
+    private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
+    private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
+    private long mModificationTimestamp = 0;
+    private GetMethod mGet;
+
+    
+    public DownloadFileOperation(Account account, OCFile file) {
+        if (account == null)
+            throw new IllegalArgumentException("Illegal null account in DownloadFileOperation creation");
+        if (file == null)
+            throw new IllegalArgumentException("Illegal null file in DownloadFileOperation creation");
+        
+        mAccount = account;
+        mFile = file;
+    }
+
+
+    public Account getAccount() {
+        return mAccount;
+    }
+    
+    public OCFile getFile() {
+        return mFile;
+    }
+
+    public String getSavePath() {
+        String path = mFile.getStoragePath();   // re-downloads should be done over the original file 
+        if (path != null && path.length() > 0) {
+            return path;
+        }
+        return FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile);
+    }
+    
+    public String getTmpPath() {
+        return FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath();
+    }
+    
+    public String getRemotePath() {
+        return mFile.getRemotePath();
+    }
+
+    public String getMimeType() {
+        String mimeType = mFile.getMimetype();
+        if (mimeType == null || mimeType.length() <= 0) {
+            try {
+                mimeType = MimeTypeMap.getSingleton()
+                    .getMimeTypeFromExtension(
+                            mFile.getRemotePath().substring(mFile.getRemotePath().lastIndexOf('.') + 1));
+            } catch (IndexOutOfBoundsException e) {
+                Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + mFile.getRemotePath());
+            }
+        }
+        if (mimeType == null) {
+            mimeType = "application/octet-stream";
+        }
+        return mimeType;
+    }
+    
+    public long getSize() {
+        return mFile.getFileLength();
+    }
+    
+    public long getModificationTimestamp() {
+        return (mModificationTimestamp > 0) ? mModificationTimestamp : mFile.getModificationTimestamp();
+    }
+    
+    
+    public void addDatatransferProgressListener (OnDatatransferProgressListener 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;
+        File newFile = null;
+        boolean moved = true;
+        
+        /// download will be performed to a temporal file, then moved to the final location
+        File tmpFile = new File(getTmpPath());
+        
+        /// perform the download
+        try {
+            tmpFile.getParentFile().mkdirs();
+            int status = downloadFile(client, tmpFile);
+            if (isSuccess(status)) {
+                newFile = new File(getSavePath());
+                newFile.getParentFile().mkdirs();
+                moved = tmpFile.renameTo(newFile);
+            }
+            if (!moved)
+                result = new RemoteOperationResult(RemoteOperationResult.ResultCode.LOCAL_STORAGE_NOT_MOVED);
+            else
+                result = new RemoteOperationResult(isSuccess(status), status, (mGet != null ? mGet.getResponseHeaders() : null));
+            Log_OC.i(TAG, "Download of " + mFile.getRemotePath() + " to " + getSavePath() + ": " + result.getLogMessage());
+            
+        } catch (Exception e) {
+            result = new RemoteOperationResult(e);
+            Log_OC.e(TAG, "Download of " + mFile.getRemotePath() + " to " + getSavePath() + ": " + result.getLogMessage(), e);
+        }
+        
+        return result;
+    }
+
+    
+    public boolean isSuccess(int status) {
+        return (status == HttpStatus.SC_OK);
+    }
+    
+    
+    protected int downloadFile(WebdavClient client, File targetFile) throws HttpException, IOException, OperationCancelledException {
+        int status = -1;
+        boolean savedFile = false;
+        mGet = new GetMethod(client.getBaseUri() + WebdavUtils.encodePath(mFile.getRemotePath()));
+        Iterator<OnDatatransferProgressListener> it = null;
+        
+        FileOutputStream fos = null;
+        try {
+            status = client.executeMethod(mGet);
+            if (isSuccess(status)) {
+                targetFile.createNewFile();
+                BufferedInputStream bis = new BufferedInputStream(mGet.getResponseBodyAsStream());
+                fos = new FileOutputStream(targetFile);
+                long transferred = 0;
+
+                byte[] bytes = new byte[4096];
+                int readResult = 0;
+                while ((readResult = bis.read(bytes)) != -1) {
+                    synchronized(mCancellationRequested) {
+                        if (mCancellationRequested.get()) {
+                            mGet.abort();
+                            throw new OperationCancelledException();
+                        }
+                    }
+                    fos.write(bytes, 0, readResult);
+                    transferred += readResult;
+                    synchronized (mDataTransferListeners) {
+                        it = mDataTransferListeners.iterator();
+                        while (it.hasNext()) {
+                            it.next().onTransferProgress(readResult, transferred, mFile.getFileLength(), targetFile.getName());
+                        }
+                    }
+                }
+                savedFile = true;
+                Header modificationTime = mGet.getResponseHeader("Last-Modified");
+                if (modificationTime != null) {
+                    Date d = WebdavUtils.parseResponseDate((String) modificationTime.getValue());
+                    mModificationTimestamp = (d != null) ? d.getTime() : 0;
+                }
+                
+            } else {
+                client.exhaustResponse(mGet.getResponseBodyAsStream());
+            }
+                
+        } finally {
+            if (fos != null) fos.close();
+            if (!savedFile && targetFile.exists()) {
+                targetFile.delete();
+            }
+            mGet.releaseConnection();    // let the connection available for other methods
+        }
+        return status;
+    }
+
+    
+    public void cancel() {
+        mCancellationRequested.set(true);   // atomic set; there is no need of synchronizing it
+    }
+
+
+}
diff --git a/src/com/owncloud/android/operations/ExistenceCheckOperation.java b/src/com/owncloud/android/operations/ExistenceCheckOperation.java
new file mode 100644 (file)
index 0000000..24336bd
--- /dev/null
@@ -0,0 +1,96 @@
+/* ownCloud Android client application
+ *   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 version 2,
+ *   as published by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package com.owncloud.android.operations;
+
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.httpclient.methods.HeadMethod;
+
+import com.owncloud.android.Log_OC;
+
+
+import eu.alefzero.webdav.WebdavClient;
+import eu.alefzero.webdav.WebdavUtils;
+import android.content.Context;
+import android.net.ConnectivityManager;
+
+/**
+ * Operation to check the existence or absence of a path in a remote server.
+ * 
+ * @author David A. Velasco
+ */
+public class ExistenceCheckOperation extends RemoteOperation {
+    
+    /** Maximum time to wait for a response from the server in MILLISECONDs.  */
+    public static final int TIMEOUT = 10000;
+    
+    private static final String TAG = ExistenceCheckOperation.class.getSimpleName();
+    
+    private String mPath;
+    private Context mContext;
+    private boolean mSuccessIfAbsent;
+
+    
+    /**
+     * Full constructor. Success of the operation will depend upon the value of successIfAbsent.
+     * 
+     * @param path              Path to append to the URL owned by the client instance.
+     * @param context           Android application context.
+     * @param successIfAbsent   When 'true', the operation finishes in success if the path does NOT exist in the remote server (HTTP 404).
+     */
+    public ExistenceCheckOperation(String path, Context context, boolean successIfAbsent) {
+        mPath = (path != null) ? path : "";
+        mContext = context;
+        mSuccessIfAbsent = successIfAbsent;
+    }
+    
+
+       @Override
+       protected RemoteOperationResult run(WebdavClient client) {
+        if (!isOnline()) {
+            return new RemoteOperationResult(RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION);
+        }
+        RemoteOperationResult result = null;
+        HeadMethod head = null;
+        try {
+            head = new HeadMethod(client.getBaseUri() + WebdavUtils.encodePath(mPath));
+            int status = client.executeMethod(head, TIMEOUT, TIMEOUT);
+            client.exhaustResponse(head.getResponseBodyAsStream());
+            boolean success = (status == HttpStatus.SC_OK && !mSuccessIfAbsent) || (status == HttpStatus.SC_NOT_FOUND && mSuccessIfAbsent);
+            result = new RemoteOperationResult(success, status, head.getResponseHeaders());
+            Log_OC.d(TAG, "Existence check for " + client.getBaseUri() + WebdavUtils.encodePath(mPath) + " targeting for " + (mSuccessIfAbsent ? " absence " : " existence ") + "finished with HTTP status " + status + (!success?"(FAIL)":""));
+            
+        } catch (Exception e) {
+            result = new RemoteOperationResult(e);
+            Log_OC.e(TAG, "Existence check for " + client.getBaseUri() + WebdavUtils.encodePath(mPath) + " targeting for " + (mSuccessIfAbsent ? " absence " : " existence ") + ": " + result.getLogMessage(), result.getException());
+            
+        } finally {
+            if (head != null)
+                head.releaseConnection();
+        }
+        return result;
+       }
+
+    private boolean isOnline() {
+        ConnectivityManager cm = (ConnectivityManager) mContext
+                .getSystemService(Context.CONNECTIVITY_SERVICE);
+        return cm != null && cm.getActiveNetworkInfo() != null
+                && cm.getActiveNetworkInfo().isConnectedOrConnecting();
+    }
+
+
+}
diff --git a/src/com/owncloud/android/operations/OAuth2GetAccessToken.java b/src/com/owncloud/android/operations/OAuth2GetAccessToken.java
new file mode 100644 (file)
index 0000000..09a6e1e
--- /dev/null
@@ -0,0 +1,175 @@
+package com.owncloud.android.operations;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.httpclient.methods.PostMethod;
+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 eu.alefzero.webdav.WebdavClient;
+
+public class OAuth2GetAccessToken extends RemoteOperation {
+    
+    private static final String TAG = OAuth2GetAccessToken.class.getSimpleName();
+    
+    private String mClientId;
+    private String mRedirectUri;
+    private String mGrantType;
+    
+    private String mOAuth2AuthorizationResponse;
+    private Map<String, String> mOAuth2ParsedAuthorizationResponse;
+    private Map<String, String> mResultTokenMap;
+
+    
+    public OAuth2GetAccessToken(String clientId, String redirectUri, String grantType, String oAuth2AuthorizationResponse) {
+        mClientId = clientId;
+        mRedirectUri = redirectUri;
+        mGrantType = grantType;
+        mOAuth2AuthorizationResponse = oAuth2AuthorizationResponse;
+        mOAuth2ParsedAuthorizationResponse = new HashMap<String, String>();
+        mResultTokenMap = null;
+    }
+    
+    
+    public Map<String, String> getOauth2AutorizationResponse() {
+        return mOAuth2ParsedAuthorizationResponse;
+    }
+
+    public Map<String, String> getResultTokenMap() {
+        return mResultTokenMap;
+    }
+    
+    @Override
+    protected RemoteOperationResult run(WebdavClient client) {
+        RemoteOperationResult result = null;
+        PostMethod postMethod = null;
+        
+        try {
+            parseAuthorizationResponse();
+            if (mOAuth2ParsedAuthorizationResponse.keySet().contains(OAuth2Constants.KEY_ERROR)) {
+                if (OAuth2Constants.VALUE_ERROR_ACCESS_DENIED.equals(mOAuth2ParsedAuthorizationResponse.get(OAuth2Constants.KEY_ERROR))) {
+                    result = new RemoteOperationResult(ResultCode.OAUTH2_ERROR_ACCESS_DENIED);
+                } else {
+                    result = new RemoteOperationResult(ResultCode.OAUTH2_ERROR);
+                }
+            }
+            
+            if (result == null) { 
+                NameValuePair[] nameValuePairs = new NameValuePair[4];
+                nameValuePairs[0] = new NameValuePair(OAuth2Constants.KEY_GRANT_TYPE, mGrantType);
+                nameValuePairs[1] = new NameValuePair(OAuth2Constants.KEY_CODE, mOAuth2ParsedAuthorizationResponse.get(OAuth2Constants.KEY_CODE));            
+                nameValuePairs[2] = new NameValuePair(OAuth2Constants.KEY_REDIRECT_URI, mRedirectUri);       
+                nameValuePairs[3] = new NameValuePair(OAuth2Constants.KEY_CLIENT_ID, mClientId);
+                //nameValuePairs[4] = new NameValuePair(OAuth2Constants.KEY_SCOPE, mOAuth2ParsedAuthorizationResponse.get(OAuth2Constants.KEY_SCOPE));         
+                
+                postMethod = new PostMethod(client.getBaseUri().toString());
+                postMethod.setRequestBody(nameValuePairs);
+                int status = client.executeMethod(postMethod);
+                
+                String response = postMethod.getResponseBodyAsString();
+                if (response != null && response.length() > 0) {
+                    JSONObject tokenJson = new JSONObject(response);
+                    parseAccessTokenResult(tokenJson);
+                    if (mResultTokenMap.get(OAuth2Constants.KEY_ERROR) != null || mResultTokenMap.get(OAuth2Constants.KEY_ACCESS_TOKEN) == null) {
+                        result = new RemoteOperationResult(ResultCode.OAUTH2_ERROR);
+                    
+                    } else {
+                        result = new RemoteOperationResult(true, status, postMethod.getResponseHeaders());
+                    }
+                    
+                } else {
+                    client.exhaustResponse(postMethod.getResponseBodyAsStream());
+                    result = new RemoteOperationResult(false, status, postMethod.getResponseHeaders());
+                }
+            }
+            
+        } catch (Exception e) {
+            result = new RemoteOperationResult(e);
+            
+        } finally {
+            if (postMethod != null)
+                postMethod.releaseConnection();    // let the connection available for other methods
+            
+            if (result.isSuccess()) {
+                Log_OC.i(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + result.getLogMessage());
+            
+            } else if (result.getException() != null) {
+                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_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_OC.e(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + result.getLogMessage());
+            }
+        }
+        
+        return result;
+    }
+    
+    
+    private void parseAuthorizationResponse() {
+        String[] pairs = mOAuth2AuthorizationResponse.split("&");
+        int i = 0;
+        String key = "";
+        String value = "";
+        StringBuilder sb = new StringBuilder();
+        while (pairs.length > i) {
+            int j = 0;
+            String[] part = pairs[i].split("=");
+            while (part.length > j) {
+                String p = part[j];
+                if (j == 0) {
+                    key = p;
+                    sb.append(key + " = ");
+                } else if (j == 1) {
+                    value = p;
+                    mOAuth2ParsedAuthorizationResponse.put(key, value);
+                    sb.append(value + "\n");
+                }
+
+                Log_OC.v(TAG, "[" + i + "," + j + "] = " + p);
+                j++;
+            }
+            i++;
+        }
+    }
+
+
+    private void parseAccessTokenResult (JSONObject tokenJson) throws JSONException {
+        mResultTokenMap = new HashMap<String, String>();
+        
+        if (tokenJson.has(OAuth2Constants.KEY_ACCESS_TOKEN)) {
+            mResultTokenMap.put(OAuth2Constants.KEY_ACCESS_TOKEN, tokenJson.getString(OAuth2Constants.KEY_ACCESS_TOKEN));
+        }
+        if (tokenJson.has(OAuth2Constants.KEY_TOKEN_TYPE)) {
+            mResultTokenMap.put(OAuth2Constants.KEY_TOKEN_TYPE, tokenJson.getString(OAuth2Constants.KEY_TOKEN_TYPE));
+        }
+        if (tokenJson.has(OAuth2Constants.KEY_EXPIRES_IN)) {
+            mResultTokenMap.put(OAuth2Constants.KEY_EXPIRES_IN, tokenJson.getString(OAuth2Constants.KEY_EXPIRES_IN));
+        }
+        if (tokenJson.has(OAuth2Constants.KEY_REFRESH_TOKEN)) {
+            mResultTokenMap.put(OAuth2Constants.KEY_REFRESH_TOKEN, tokenJson.getString(OAuth2Constants.KEY_REFRESH_TOKEN));
+        }
+        if (tokenJson.has(OAuth2Constants.KEY_SCOPE)) {
+            mResultTokenMap.put(OAuth2Constants.KEY_SCOPE, tokenJson.getString(OAuth2Constants.KEY_SCOPE));
+        }
+        if (tokenJson.has(OAuth2Constants.KEY_ERROR)) {
+            mResultTokenMap.put(OAuth2Constants.KEY_ERROR, tokenJson.getString(OAuth2Constants.KEY_ERROR));
+        }
+        if (tokenJson.has(OAuth2Constants.KEY_ERROR_DESCRIPTION)) {
+            mResultTokenMap.put(OAuth2Constants.KEY_ERROR_DESCRIPTION, tokenJson.getString(OAuth2Constants.KEY_ERROR_DESCRIPTION));
+        }
+        if (tokenJson.has(OAuth2Constants.KEY_ERROR_URI)) {
+            mResultTokenMap.put(OAuth2Constants.KEY_ERROR_URI, tokenJson.getString(OAuth2Constants.KEY_ERROR_URI));
+        }
+    }
+
+}
diff --git a/src/com/owncloud/android/operations/OnRemoteOperationListener.java b/src/com/owncloud/android/operations/OnRemoteOperationListener.java
new file mode 100644 (file)
index 0000000..e6a58e7
--- /dev/null
@@ -0,0 +1,25 @@
+/* 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.operations;
+
+public interface OnRemoteOperationListener {
+
+       void onRemoteOperationFinish(RemoteOperation caller, RemoteOperationResult result);
+       
+}
diff --git a/src/com/owncloud/android/operations/OperationCancelledException.java b/src/com/owncloud/android/operations/OperationCancelledException.java
new file mode 100644 (file)
index 0000000..0b7878c
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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.operations;
+
+public class OperationCancelledException extends Exception {
+
+    /**
+     * Generated serial version - to avoid Java warning
+     */
+    private static final long serialVersionUID = -6350981497740424983L;
+
+}
diff --git a/src/com/owncloud/android/operations/OwnCloudServerCheckOperation.java b/src/com/owncloud/android/operations/OwnCloudServerCheckOperation.java
new file mode 100644 (file)
index 0000000..6234224
--- /dev/null
@@ -0,0 +1,138 @@
+/* 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.operations;
+
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.utils.OwnCloudVersion;
+
+
+import eu.alefzero.webdav.WebdavClient;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.Uri;
+
+public class OwnCloudServerCheckOperation extends RemoteOperation {
+    
+    /** Maximum time to wait for a response from the server when the connection is being tested, in MILLISECONDs.  */
+    public static final int TRY_CONNECTION_TIMEOUT = 5000;
+    
+    private static final String TAG = OwnCloudServerCheckOperation.class.getSimpleName();
+    
+    private String mUrl;
+    private RemoteOperationResult mLatestResult;
+    private Context mContext;
+    private OwnCloudVersion mOCVersion;
+
+    public OwnCloudServerCheckOperation(String url, Context context) {
+        mUrl = url;
+        mContext = context;
+        mOCVersion = null;
+    }
+    
+    public OwnCloudVersion getDiscoveredVersion() {
+        return mOCVersion;
+    }
+
+    private boolean tryConnection(WebdavClient wc, String urlSt) {
+        boolean retval = false;
+        GetMethod get = null;
+        try {
+            get = new GetMethod(urlSt);
+            int status = wc.executeMethod(get, TRY_CONNECTION_TIMEOUT, TRY_CONNECTION_TIMEOUT);
+            String response = get.getResponseBodyAsString();
+            if (status == HttpStatus.SC_OK) {
+                JSONObject json = new JSONObject(response);
+                if (!json.getBoolean("installed")) {
+                    mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
+                } else {
+                    mOCVersion = new OwnCloudVersion(json.getString("version"));
+                    if (!mOCVersion.isVersionValid()) {
+                        mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.BAD_OC_VERSION);
+                        
+                    } else {
+                        mLatestResult = new RemoteOperationResult(urlSt.startsWith("https://") ? 
+                                                                    RemoteOperationResult.ResultCode.OK_SSL : 
+                                                                    RemoteOperationResult.ResultCode.OK_NO_SSL
+                            );
+
+                        retval = true;
+                    }
+                }
+                
+            } else {
+                mLatestResult = new RemoteOperationResult(false, status, get.getResponseHeaders());
+            }
+
+        } catch (JSONException e) {
+            mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
+            
+        } catch (Exception e) {
+            mLatestResult = new RemoteOperationResult(e);
+            
+        } finally {
+            if (get != null)
+                get.releaseConnection();
+        }
+        
+        if (mLatestResult.isSuccess()) {
+            Log_OC.i(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage());
+            
+        } else if (mLatestResult.getException() != null) {
+            Log_OC.e(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage(), mLatestResult.getException());
+            
+        } else {
+            Log_OC.e(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage());
+        }
+
+        return retval;
+    }
+
+    private boolean isOnline() {
+        ConnectivityManager cm = (ConnectivityManager) mContext
+                .getSystemService(Context.CONNECTIVITY_SERVICE);
+        return cm != null && cm.getActiveNetworkInfo() != null
+                && cm.getActiveNetworkInfo().isConnectedOrConnecting();
+    }
+
+       @Override
+       protected RemoteOperationResult run(WebdavClient client) {
+        if (!isOnline()) {
+               return new RemoteOperationResult(RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION);
+        }
+        if (mUrl.startsWith("http://") || mUrl.startsWith("https://")) {
+            tryConnection(client, mUrl + AccountUtils.STATUS_PATH);
+            
+        } else {
+            client.setBaseUri(Uri.parse("https://" + mUrl + AccountUtils.STATUS_PATH));
+            boolean httpsSuccess = tryConnection(client, "https://" + mUrl + AccountUtils.STATUS_PATH); 
+            if (!httpsSuccess && !mLatestResult.isSslRecoverableException()) {
+                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);
+            }
+        }
+        return mLatestResult;
+       }
+       
+}
diff --git a/src/com/owncloud/android/operations/RemoteOperation.java b/src/com/owncloud/android/operations/RemoteOperation.java
new file mode 100644 (file)
index 0000000..fbb93e6
--- /dev/null
@@ -0,0 +1,293 @@
+/* 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.operations;
+
+import java.io.IOException;
+
+import org.apache.commons.httpclient.Credentials;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.network.BearerCredentials;
+import com.owncloud.android.network.OwnCloudClientUtils;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountsException;
+import android.app.Activity;
+import android.content.Context;
+import android.os.Handler;
+
+import eu.alefzero.webdav.WebdavClient;
+
+/**
+ * Operation which execution involves one or several interactions with an ownCloud server.
+ * 
+ * Provides methods to execute the operation both synchronously or asynchronously.
+ * 
+ * @author David A. Velasco 
+ */
+public abstract class RemoteOperation implements Runnable {
+       
+    private static final String TAG = RemoteOperation.class.getSimpleName();
+
+    /** ownCloud account in the remote ownCloud server to operate */
+    private Account mAccount = null;
+    
+    /** Android Application context */
+    private Context mContext = null;
+    
+       /** Object to interact with the remote server */
+       private WebdavClient mClient = null;
+       
+       /** Callback object to notify about the execution of the remote operation */
+       private OnRemoteOperationListener mListener = null;
+       
+       /** Handler to the thread where mListener methods will be called */
+       private Handler mListenerHandler = null;
+
+       /** Activity */
+    private Activity mCallerActivity;
+
+       
+       /**
+        *  Abstract method to implement the operation in derived classes.
+        */
+       protected abstract RemoteOperationResult run(WebdavClient client); 
+       
+
+    /**
+     * Synchronously executes the remote operation on the received ownCloud account.
+     * 
+     * Do not call this method from the main thread.
+     * 
+     * This method should be used whenever an ownCloud account is available, instead of {@link #execute(WebdavClient)}. 
+     * 
+     * @param account   ownCloud account in remote ownCloud server to reach during the execution of the operation.
+     * @param context   Android context for the component calling the method.
+     * @return          Result of the operation.
+     */
+    public final RemoteOperationResult execute(Account account, Context context) {
+        if (account == null)
+            throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account");
+        if (context == null)
+            throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context");
+        mAccount = account;
+        mContext = context.getApplicationContext();
+        try {
+            mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext);
+        } catch (Exception e) {
+            Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, e);
+            return new RemoteOperationResult(e);
+        }
+        return run(mClient);
+    }
+    
+       
+       /**
+        * Synchronously executes the remote operation
+        * 
+     * Do not call this method from the main thread.
+     * 
+        * @param client        Client object to reach an ownCloud server during the execution of the operation.
+        * @return                      Result of the operation.
+        */
+       public final RemoteOperationResult execute(WebdavClient client) {
+               if (client == null)
+                       throw new IllegalArgumentException("Trying to execute a remote operation with a NULL WebdavClient");
+               mClient = client;
+               return run(client);
+       }
+
+       
+    /**
+     * Asynchronously executes the remote operation
+     * 
+     * This method should be used whenever an ownCloud account is available, instead of {@link #execute(WebdavClient)}. 
+     * 
+     * @param account           ownCloud account in remote ownCloud server to reach during the execution of the operation.
+     * @param context           Android context for the component calling the method.
+     * @param listener          Listener to be notified about the execution of the operation.
+     * @param listenerHandler   Handler associated to the thread where the methods of the listener objects must be called.
+     * @return                  Thread were the remote operation is executed.
+     */
+    public final Thread execute(Account account, Context context, OnRemoteOperationListener listener, Handler listenerHandler, Activity callerActivity) {
+        if (account == null)
+            throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account");
+        if (context == null)
+            throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context");
+        mAccount = account;
+        mContext = context.getApplicationContext();
+        mCallerActivity = callerActivity;
+        mClient = null;     // the client instance will be created from mAccount and mContext in the runnerThread to create below
+        
+        if (listener == null) {
+            throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a listener to notiy the result");
+        }
+        mListener = listener;
+        
+        if (listenerHandler == null) {
+            throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a handler to the listener's thread");
+        }
+        mListenerHandler = listenerHandler;
+        
+        Thread runnerThread = new Thread(this);
+        runnerThread.start();
+        return runnerThread;
+    }
+
+    
+       /**
+        * Asynchronously executes the remote operation
+        * 
+        * @param client                        Client object to reach an ownCloud server during the execution of the operation.
+        * @param listener                      Listener to be notified about the execution of the operation.
+        * @param listenerHandler       Handler associated to the thread where the methods of the listener objects must be called.
+        * @return                                      Thread were the remote operation is executed.
+        */
+       public final Thread execute(WebdavClient client, OnRemoteOperationListener listener, Handler listenerHandler) {
+               if (client == null) {
+                       throw new IllegalArgumentException("Trying to execute a remote operation with a NULL WebdavClient");
+               }
+               mClient = client;
+               
+               if (listener == null) {
+                       throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a listener to notiy the result");
+               }
+               mListener = listener;
+               
+               if (listenerHandler == null) {
+                       throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a handler to the listener's thread");
+               }
+               mListenerHandler = listenerHandler;
+               
+               Thread runnerThread = new Thread(this);
+               runnerThread.start();
+               return runnerThread;
+       }
+       
+    /**
+     * Synchronously retries the remote operation using the same WebdavClient in the last call to {@link RemoteOperation#execute(WebdavClient)}
+     * 
+     * @param listener          Listener to be notified about the execution of the operation.
+     * @param listenerHandler   Handler associated to the thread where the methods of the listener objects must be called.
+     * @return                  Thread were the remote operation is executed.
+     */
+    public final RemoteOperationResult retry() {
+        return execute(mClient);
+    }
+    
+    /**
+     * Asynchronously retries the remote operation using the same WebdavClient in the last call to {@link RemoteOperation#execute(WebdavClient, OnRemoteOperationListener, Handler)}
+     * 
+     * @param listener          Listener to be notified about the execution of the operation.
+     * @param listenerHandler   Handler associated to the thread where the methods of the listener objects must be called.
+     * @return                  Thread were the remote operation is executed.
+     */
+    public final Thread retry(OnRemoteOperationListener listener, Handler listenerHandler) {
+        return execute(mClient, listener, listenerHandler);
+    }
+       
+       
+       /**
+        * Asynchronous execution of the operation 
+        * started by {@link RemoteOperation#execute(WebdavClient, OnRemoteOperationListener, Handler)}, 
+        * and result posting.
+        * 
+        * TODO refactor && clean the code; now it's a mess
+        */
+    @Override
+    public final void run() {
+        RemoteOperationResult result = null;
+        boolean repeat = false;
+        do {
+            try{
+                if (mClient == null) {
+                    if (mAccount != null && mContext != null) {
+                        if (mCallerActivity != null) {
+                            mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext, mCallerActivity);
+                        } else {
+                            mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext);
+                        }
+                    } else {
+                        throw new IllegalStateException("Trying to run a remote operation asynchronously with no client instance or account");
+                    }
+                }
+            
+            } catch (IOException 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_OC.e(TAG, "Error while trying to access to " + mAccount.name, e);
+                result = new RemoteOperationResult(e);
+            }
+       
+            if (result == null)
+                result = run(mClient);
+        
+            repeat = false;
+            if (mCallerActivity != null && mAccount != null && mContext != null && !result.isSuccess() &&
+//                    (result.getCode() == ResultCode.UNAUTHORIZED || (result.isTemporalRedirection() && result.isIdPRedirection()))) {
+                    (result.getCode() == ResultCode.UNAUTHORIZED || result.isIdPRedirection())) {
+                /// possible fail due to lack of authorization in an operation performed in foreground
+                Credentials cred = mClient.getCredentials();
+                String ssoSessionCookie = mClient.getSsoSessionCookie();
+                if (cred != null || ssoSessionCookie != null) {
+                    /// confirmed : unauthorized operation
+                    AccountManager am = AccountManager.get(mContext);
+                    boolean bearerAuthorization = (cred != null && cred instanceof BearerCredentials);
+                    boolean samlBasedSsoAuthorization = (cred == null && ssoSessionCookie != null);
+                    if (bearerAuthorization) {
+                        am.invalidateAuthToken(MainApp.getAccountType(), ((BearerCredentials)cred).getAccessToken());
+                    } else if (samlBasedSsoAuthorization ) {
+                        am.invalidateAuthToken(MainApp.getAccountType(), ssoSessionCookie);
+                    } else {
+                        am.clearPassword(mAccount);
+                    }
+                    mClient = null;
+                    repeat = true;  // when repeated, the creation of a new OwnCloudClient after erasing the saved credentials will trigger the login activity
+                    result = null;
+                }
+            }
+        } while (repeat);
+        
+        final RemoteOperationResult resultToSend = result;
+        if (mListenerHandler != null && mListener != null) {
+               mListenerHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend);
+                }
+            });
+        }
+    }
+
+
+    /**
+     * Returns the current client instance to access the remote server.
+     * 
+     * @return      Current client instance to access the remote server.
+     */
+    public final WebdavClient getClient() {
+        return mClient;
+    }
+
+
+}
diff --git a/src/com/owncloud/android/operations/RemoteOperationResult.java b/src/com/owncloud/android/operations/RemoteOperationResult.java
new file mode 100644 (file)
index 0000000..8d9b3ee
--- /dev/null
@@ -0,0 +1,338 @@
+/* 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.operations;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.MalformedURLException;
+import java.net.SocketException;
+import java.net.SocketTimeoutException;
+import java.net.UnknownHostException;
+
+import javax.net.ssl.SSLException;
+
+import org.apache.commons.httpclient.ConnectTimeoutException;
+import org.apache.commons.httpclient.Header;
+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.authentication.AccountUtils.AccountNotFoundException;
+import com.owncloud.android.network.CertificateCombinedException;
+
+import android.accounts.Account;
+import android.accounts.AccountsException;
+
+
+/**
+ * The result of a remote operation required to an ownCloud server.
+ * 
+ * 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 = -4415103901492836870L;
+    
+
+    
+    private static final String TAG = "RemoteOperationResult";
+    
+    public enum ResultCode { 
+        OK,
+        OK_SSL,
+        OK_NO_SSL,
+        UNHANDLED_HTTP_CODE,
+        UNAUTHORIZED,        
+        FILE_NOT_FOUND, 
+        INSTANCE_NOT_CONFIGURED, 
+        UNKNOWN_ERROR, 
+        WRONG_CONNECTION,  
+        TIMEOUT, 
+        INCORRECT_ADDRESS, 
+        HOST_NOT_AVAILABLE, 
+        NO_NETWORK_CONNECTION, 
+        SSL_ERROR,
+        SSL_RECOVERABLE_PEER_UNVERIFIED,
+        BAD_OC_VERSION,
+        CANCELLED, 
+        INVALID_LOCAL_FILE_NAME, 
+        INVALID_OVERWRITE,
+        CONFLICT, 
+        OAUTH2_ERROR,
+        SYNC_CONFLICT,
+        LOCAL_STORAGE_FULL, 
+        LOCAL_STORAGE_NOT_MOVED, 
+        LOCAL_STORAGE_NOT_COPIED, 
+        OAUTH2_ERROR_ACCESS_DENIED,
+        QUOTA_EXCEEDED, 
+        ACCOUNT_NOT_FOUND, 
+        ACCOUNT_EXCEPTION, 
+        ACCOUNT_NOT_NEW, 
+        ACCOUNT_NOT_THE_SAME
+    }
+
+    private boolean mSuccess = false;
+    private int mHttpCode = -1;
+    private Exception mException = null;
+    private ResultCode mCode = ResultCode.UNKNOWN_ERROR;
+    private String mRedirectedLocation;
+
+    public RemoteOperationResult(ResultCode code) {
+        mCode = code;
+        mSuccess = (code == ResultCode.OK || code == ResultCode.OK_SSL || code == ResultCode.OK_NO_SSL);
+    }
+
+    private RemoteOperationResult(boolean success, int httpCode) {
+        mSuccess = success;
+        mHttpCode = httpCode;
+
+        if (success) {
+            mCode = ResultCode.OK;
+
+        } else if (httpCode > 0) {
+            switch (httpCode) {
+            case HttpStatus.SC_UNAUTHORIZED:
+                mCode = ResultCode.UNAUTHORIZED;
+                break;
+            case HttpStatus.SC_NOT_FOUND:
+                mCode = ResultCode.FILE_NOT_FOUND;
+                break;
+            case HttpStatus.SC_INTERNAL_SERVER_ERROR:
+                mCode = ResultCode.INSTANCE_NOT_CONFIGURED;
+                break;
+            case HttpStatus.SC_CONFLICT:
+                mCode = ResultCode.CONFLICT;
+                break;
+            case HttpStatus.SC_INSUFFICIENT_STORAGE:
+                mCode = ResultCode.QUOTA_EXCEEDED;
+                break;
+            default:
+                mCode = ResultCode.UNHANDLED_HTTP_CODE;
+                Log_OC.d(TAG, "RemoteOperationResult has processed UNHANDLED_HTTP_CODE: " + httpCode);
+            }
+        }
+    }
+    
+    public RemoteOperationResult(boolean success, int httpCode, Header[] headers) {
+        this(success, httpCode);
+        if (headers != null) {
+            Header current;
+            for (int i=0; i<headers.length; i++) {
+                current = headers[i];
+                if ("Location".equals(current.getName())) {
+                    mRedirectedLocation = current.getValue();
+                    break;
+                }
+            }
+        }
+    }    
+
+    public RemoteOperationResult(Exception e) {
+        mException = e;
+
+        if (e instanceof OperationCancelledException) {
+            mCode = ResultCode.CANCELLED;
+
+        } 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 AccountNotFoundException) {
+            mCode = ResultCode.ACCOUNT_NOT_FOUND;
+            
+        } else if (e instanceof AccountsException) {
+            mCode = ResultCode.ACCOUNT_EXCEPTION;
+            
+        } else if (e instanceof SSLException || e instanceof RuntimeException) {
+            CertificateCombinedException se = getCertificateCombinedException(e);
+            if (se != null) {
+                mException = se;
+                if (se.isRecoverable()) {
+                    mCode = ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED;
+                }
+            } 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;
+    }
+
+    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;
+        }
+        Throwable cause = mException.getCause();
+        Throwable previousCause = null;
+        while (cause != null && cause != previousCause && !(cause instanceof CertificateCombinedException)) {
+            previousCause = cause;
+            cause = cause.getCause();
+        }
+        if (cause != null && cause instanceof CertificateCombinedException) {
+            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) {
+                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";
+
+            } else if (mException instanceof DavException) {
+                return "Unexpected WebDAV exception";
+
+            } else if (mException instanceof HttpException) {
+                return "HTTP violation";
+
+            } else if (mException instanceof IOException) {
+                return "Unrecovered transport exception";
+
+            } else if (mException instanceof AccountNotFoundException) {
+                Account failedAccount = ((AccountNotFoundException)mException).getFailedAccount();
+                return mException.getMessage() + " (" + (failedAccount != null ? failedAccount.name : "NULL") + ")";
+                
+            } else if (mException instanceof AccountsException) {
+                return "Exception while using account";
+                
+            } else {
+                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";
+
+        } else if (mCode == ResultCode.ACCOUNT_NOT_NEW) {
+            return "Account already existing when creating a new one";
+
+        } else if (mCode == ResultCode.ACCOUNT_NOT_THE_SAME) {
+            return "Authenticated with a different account than the one updating";
+        }
+
+        return "Operation finished with HTTP status code " + mHttpCode + " (" + (isSuccess() ? "success" : "fail") + ")";
+
+    }
+
+    public boolean isServerFail() {
+        return (mHttpCode >= HttpStatus.SC_INTERNAL_SERVER_ERROR);
+    }
+
+    public boolean isException() {
+        return (mException != null);
+    }
+
+    public boolean isTemporalRedirection() {
+        return (mHttpCode == 302 || mHttpCode == 307);
+    }
+
+    public String getRedirectedLocation() {
+        return mRedirectedLocation;
+    }
+    
+    public boolean isIdPRedirection() {
+        return (mRedirectedLocation != null &&
+                (mRedirectedLocation.toUpperCase().contains("SAML") || 
+                mRedirectedLocation.toLowerCase().contains("wayf")));
+    }
+
+}
diff --git a/src/com/owncloud/android/operations/RemoveFileOperation.java b/src/com/owncloud/android/operations/RemoveFileOperation.java
new file mode 100644 (file)
index 0000000..f8a6c4a
--- /dev/null
@@ -0,0 +1,106 @@
+/* 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.operations;
+
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.jackrabbit.webdav.client.methods.DeleteMethod;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+
+
+import eu.alefzero.webdav.WebdavClient;
+import eu.alefzero.webdav.WebdavUtils;
+
+/**
+ * Remote operation performing the removal of a remote file or folder in the ownCloud server.
+ * 
+ * @author David A. Velasco
+ */
+public class RemoveFileOperation extends RemoteOperation {
+    
+    private static final String TAG = RemoveFileOperation.class.getSimpleName();
+
+    private static final int REMOVE_READ_TIMEOUT = 10000;
+    private static final int REMOVE_CONNECTION_TIMEOUT = 5000;
+    
+    OCFile mFileToRemove;
+    boolean mDeleteLocalCopy;
+    DataStorageManager mDataStorageManager;
+    
+    
+    /**
+     * Constructor
+     * 
+     * @param fileToRemove          OCFile instance describing the remote file or folder to remove from the server
+     * @param deleteLocalCopy       When 'true', and a local copy of the file exists, it is also removed.
+     * @param storageManager        Reference to the local database corresponding to the account where the file is contained. 
+     */
+    public RemoveFileOperation(OCFile fileToRemove, boolean deleteLocalCopy, DataStorageManager storageManager) {
+        mFileToRemove = fileToRemove;
+        mDeleteLocalCopy = deleteLocalCopy;
+        mDataStorageManager = storageManager;
+    }
+    
+    
+    /**
+     * Getter for the file to remove (or removed, if the operation was successfully performed).
+     * 
+     * @return      File to remove or already removed.
+     */
+    public OCFile getFile() {
+        return mFileToRemove;
+    }
+    
+    
+    /**
+     * Performs the remove operation
+     * 
+     * @param   client      Client object to communicate with the remote ownCloud server.
+     */
+    @Override
+    protected RemoteOperationResult run(WebdavClient client) {
+        RemoteOperationResult result = null;
+        DeleteMethod delete = null;
+        try {
+            delete = new DeleteMethod(client.getBaseUri() + WebdavUtils.encodePath(mFileToRemove.getRemotePath()));
+            int status = client.executeMethod(delete, REMOVE_READ_TIMEOUT, REMOVE_CONNECTION_TIMEOUT);
+            if (delete.succeeded() || status == HttpStatus.SC_NOT_FOUND) {
+                if (mFileToRemove.isDirectory()) {
+                    mDataStorageManager.removeDirectory(mFileToRemove, true, mDeleteLocalCopy);
+                } else {
+                    mDataStorageManager.removeFile(mFileToRemove, mDeleteLocalCopy);
+                }
+            }
+            delete.getResponseBodyAsString();   // exhaust the response, although not interesting
+            result = new RemoteOperationResult((delete.succeeded() || status == HttpStatus.SC_NOT_FOUND), status, delete.getResponseHeaders());
+            Log_OC.i(TAG, "Remove " + mFileToRemove.getRemotePath() + ": " + result.getLogMessage());
+            
+        } catch (Exception e) {
+            result = new RemoteOperationResult(e);
+            Log_OC.e(TAG, "Remove " + mFileToRemove.getRemotePath() + ": " + result.getLogMessage(), e);
+            
+        } finally {
+            if (delete != null)
+                delete.releaseConnection();
+        }
+        return result;
+    }
+    
+}
diff --git a/src/com/owncloud/android/operations/RenameFileOperation.java b/src/com/owncloud/android/operations/RenameFileOperation.java
new file mode 100644 (file)
index 0000000..86d2712
--- /dev/null
@@ -0,0 +1,248 @@
+/* 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.operations;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.jackrabbit.webdav.client.methods.DavMethodBase;
+
+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;
+import com.owncloud.android.utils.FileStorageUtils;
+//import org.apache.jackrabbit.webdav.client.methods.MoveMethod;
+
+import android.accounts.Account;
+
+
+import eu.alefzero.webdav.WebdavClient;
+import eu.alefzero.webdav.WebdavUtils;
+
+/**
+ * Remote operation performing the rename of a remote file (or folder?) in the ownCloud server.
+ * 
+ * @author David A. Velasco
+ */
+public class RenameFileOperation extends RemoteOperation {
+    
+    private static final String TAG = RenameFileOperation.class.getSimpleName();
+
+    private static final int RENAME_READ_TIMEOUT = 10000;
+    private static final int RENAME_CONNECTION_TIMEOUT = 5000;
+    
+
+    private OCFile mFile;
+    private Account mAccount;
+    private String mNewName;
+    private String mNewRemotePath;
+    private DataStorageManager mStorageManager;
+    
+    
+    /**
+     * Constructor
+     * 
+     * @param file                  OCFile instance describing the remote file or folder to rename
+     * @param account               OwnCloud account containing the remote file 
+     * @param newName               New name to set as the name of file.
+     * @param storageManager        Reference to the local database corresponding to the account where the file is contained. 
+     */
+    public RenameFileOperation(OCFile file, Account account, String newName, DataStorageManager storageManager) {
+        mFile = file;
+        mAccount = account;
+        mNewName = newName;
+        mNewRemotePath = null;
+        mStorageManager = storageManager;
+    }
+  
+    public OCFile getFile() {
+        return mFile;
+    }
+    
+    
+    /**
+     * Performs the rename operation.
+     * 
+     * @param   client      Client object to communicate with the remote ownCloud server.
+     */
+    @Override
+    protected RemoteOperationResult run(WebdavClient client) {
+        RemoteOperationResult result = null;
+        
+        LocalMoveMethod move = null;
+        mNewRemotePath = null;
+        try {
+            if (mNewName.equals(mFile.getFileName())) {
+                return new RemoteOperationResult(ResultCode.OK);
+            }
+        
+            String parent = (new File(mFile.getRemotePath())).getParent();
+            parent = (parent.endsWith(OCFile.PATH_SEPARATOR)) ? parent : parent + OCFile.PATH_SEPARATOR; 
+            mNewRemotePath =  parent + mNewName;
+            if (mFile.isDirectory()) {
+                mNewRemotePath += OCFile.PATH_SEPARATOR;
+            }
+            
+            // check if the new name is valid in the local file system
+            if (!isValidNewName()) {
+                return new RemoteOperationResult(ResultCode.INVALID_LOCAL_FILE_NAME);
+            }
+        
+            // check if a file with the new name already exists
+            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);
+            }
+            move = new LocalMoveMethod( client.getBaseUri() + WebdavUtils.encodePath(mFile.getRemotePath()),
+                                        client.getBaseUri() + WebdavUtils.encodePath(mNewRemotePath));
+            int status = client.executeMethod(move, RENAME_READ_TIMEOUT, RENAME_CONNECTION_TIMEOUT);
+            if (move.succeeded()) {
+
+                if (mFile.isDirectory()) {
+                    saveLocalDirectory();
+                    
+                } else {
+                    saveLocalFile();
+                    
+                }
+             
+            /* 
+             *} else if (mFile.isDirectory() && (status == 207 || status >= 500)) {
+             *   // TODO 
+             *   // if server fails in the rename of a folder, some children files could have been moved to a folder with the new name while some others
+             *   // stayed in the old folder;
+             *   //
+             *   // easiest and heaviest solution is synchronizing the parent folder (or the full account);
+             *   //
+             *   // a better solution is synchronizing the folders with the old and new names;
+             *}
+             */
+                
+            }
+            
+            move.getResponseBodyAsString(); // exhaust response, although not interesting
+            result = new RemoteOperationResult(move.succeeded(), status, move.getResponseHeaders());
+            Log_OC.i(TAG, "Rename " + mFile.getRemotePath() + " to " + mNewRemotePath + ": " + result.getLogMessage());
+            
+        } catch (Exception e) {
+            result = new RemoteOperationResult(e);
+            Log_OC.e(TAG, "Rename " + mFile.getRemotePath() + " to " + ((mNewRemotePath==null) ? mNewName : mNewRemotePath) + ": " + result.getLogMessage(), e);
+            
+        } finally {
+            if (move != null)
+                move.releaseConnection();
+        }
+        return result;
+    }
+
+    
+    private void saveLocalDirectory() {
+        mStorageManager.moveDirectory(mFile, mNewRemotePath);
+        String localPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile);
+        File localDir = new File(localPath);
+        if (localDir.exists()) {
+            localDir.renameTo(new File(FileStorageUtils.getSavePath(mAccount.name) + mNewRemotePath));
+            // TODO - if renameTo fails, children files that are already down will result unlinked
+        }
+    }
+
+    private void saveLocalFile() {
+        mFile.setFileName(mNewName);
+        
+        // try to rename the local copy of the file
+        if (mFile.isDown()) {
+            File f = new File(mFile.getStoragePath());
+            String parentStoragePath = f.getParent();
+            if (!parentStoragePath.endsWith(File.separator))
+                parentStoragePath += File.separator;
+            if (f.renameTo(new File(parentStoragePath + mNewName))) {
+                mFile.setStoragePath(parentStoragePath + mNewName);
+            }
+            // else - NOTHING: the link to the local file is kept although the local name can't be updated
+            // TODO - study conditions when this could be a problem
+        }
+        
+        mStorageManager.saveFile(mFile);
+    }
+
+    /**
+     * Checks if the new name to set is valid in the file system 
+     * 
+     * The only way to be sure is trying to create a file with that name. It's made in the temporal directory
+     * for downloads, out of any account, and then removed. 
+     * 
+     * IMPORTANT: The test must be made in the same file system where files are download. The internal storage
+     * could be formatted with a different file system.
+     * 
+     * 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.
+     * @throws IOException  When the temporal folder can not be created.
+     */
+    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 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_OC.i(TAG, "Test for validity of name " + mNewName + " in the file system failed");
+            return false;
+        }
+        boolean result = (testFile.exists() && testFile.isFile());
+        
+        // cleaning ; result is ignored, since there is not much we could do in case of failure, but repeat and repeat...
+        testFile.delete();
+        
+        return result;
+    }
+
+
+    // move operation
+    private class LocalMoveMethod extends DavMethodBase {
+
+        public LocalMoveMethod(String uri, String dest) {
+            super(uri);
+            addRequestHeader(new org.apache.commons.httpclient.Header("Destination", dest));
+        }
+
+        @Override
+        public String getName() {
+            return "MOVE";
+        }
+
+        @Override
+        protected boolean isSuccess(int status) {
+            return status == 201 || status == 204;
+        }
+            
+    }
+    
+
+}
diff --git a/src/com/owncloud/android/operations/SynchronizeFileOperation.java b/src/com/owncloud/android/operations/SynchronizeFileOperation.java
new file mode 100644 (file)
index 0000000..211ba70
--- /dev/null
@@ -0,0 +1,235 @@
+/* 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.operations;
+
+import org.apache.http.HttpStatus;
+import org.apache.jackrabbit.webdav.DavConstants;
+import org.apache.jackrabbit.webdav.MultiStatus;
+import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
+
+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;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+
+import android.accounts.Account;
+import android.content.Context;
+import android.content.Intent;
+
+
+import eu.alefzero.webdav.WebdavClient;
+import eu.alefzero.webdav.WebdavEntry;
+import eu.alefzero.webdav.WebdavUtils;
+
+public class SynchronizeFileOperation extends RemoteOperation {
+
+    private String TAG = SynchronizeFileOperation.class.getSimpleName();
+    private static final int SYNC_READ_TIMEOUT = 10000;
+    private static final int SYNC_CONNECTION_TIMEOUT = 5000;
+    
+    private OCFile mLocalFile;
+    private OCFile mServerFile;
+    private DataStorageManager mStorageManager;
+    private Account mAccount;
+    private boolean mSyncFileContents;
+    private boolean mLocalChangeAlreadyKnown;
+    private Context mContext;
+    
+    private boolean mTransferWasRequested = false;
+    
+    public SynchronizeFileOperation(
+            OCFile localFile,
+            OCFile serverFile,          // make this null to let the operation checks the server; added to reuse info from SynchronizeFolderOperation 
+            DataStorageManager storageManager, 
+            Account account, 
+            boolean syncFileContents,
+            boolean localChangeAlreadyKnown, 
+            Context context) {
+        
+        mLocalFile = localFile;
+        mServerFile = serverFile;
+        mStorageManager = storageManager;
+        mAccount = account;
+        mSyncFileContents = syncFileContents;
+        mLocalChangeAlreadyKnown = localChangeAlreadyKnown;
+        mContext = context;
+    }
+
+
+    @Override
+    protected RemoteOperationResult run(WebdavClient client) {
+        
+        PropFindMethod propfind = null;
+        RemoteOperationResult result = null;
+        mTransferWasRequested = false;
+        try {
+            if (!mLocalFile.isDown()) {
+                /// easy decision
+                requestForDownload(mLocalFile);
+                result = new RemoteOperationResult(ResultCode.OK);
+                
+            } else {
+                /// local copy in the device -> need to think a bit more before do anything
+                
+                if (mServerFile == null) {
+                    /// take the duty of check the server for the current state of the file there
+                    propfind = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mLocalFile.getRemotePath()),
+                            DavConstants.PROPFIND_ALL_PROP,
+                            DavConstants.DEPTH_0);
+                    int status = client.executeMethod(propfind, SYNC_READ_TIMEOUT, SYNC_CONNECTION_TIMEOUT);
+                    boolean isMultiStatus = status == HttpStatus.SC_MULTI_STATUS;
+                    if (isMultiStatus) {
+                        MultiStatus resp = propfind.getResponseBodyAsMultiStatus();
+                        WebdavEntry we = new WebdavEntry(resp.getResponses()[0],
+                                               client.getBaseUri().getPath());
+                        mServerFile = fillOCFile(we);
+                        mServerFile.setLastSyncDateForProperties(System.currentTimeMillis());
+                        
+                    } else {
+                        client.exhaustResponse(propfind.getResponseBodyAsStream());
+                        result = new RemoteOperationResult(false, status, propfind.getResponseHeaders());
+                    }
+                }
+                
+                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;
+                    if (mServerFile.getEtag() != null) {
+                        serverChanged = (!mServerFile.getEtag().equals(mLocalFile.getEtag()));   // TODO could this be dangerous when the user upgrades the server from non-tagged to tagged?
+                    } else {
+                        // server without etags
+                        serverChanged = (mServerFile.getModificationTimestamp() > mLocalFile.getModificationTimestampAtLastSyncForData());
+                    }
+                    boolean localChanged = (mLocalChangeAlreadyKnown || mLocalFile.getLocalModificationTimestamp() > mLocalFile.getLastSyncDateForData());
+                        // TODO this will be always true after the app is upgraded to database version 2; will result in unnecessary uploads
+              
+                    /// decide action to perform depending upon changes
+                    if (localChanged && serverChanged) {
+                        result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
+                  
+                    } else if (localChanged) {
+                        if (mSyncFileContents) {
+                            requestForUpload(mLocalFile);
+                            // the local update of file properties will be done by the FileUploader service when the upload finishes
+                        } else {
+                            // NOTHING TO DO HERE: updating the properties of the file in the server without uploading the contents would be stupid; 
+                            // So, an instance of SynchronizeFileOperation created with syncFileContents == false is completely useless when we suspect
+                            // that an upload is necessary (for instance, in FileObserverService).
+                        }
+                        result = new RemoteOperationResult(ResultCode.OK);
+                  
+                    } else if (serverChanged) {
+                        if (mSyncFileContents) {
+                            requestForDownload(mLocalFile); // local, not server; we won't to keep the value of keepInSync!
+                            // the update of local data will be done later by the FileUploader service when the upload finishes
+                        } else {
+                            // TODO CHECK: is this really useful in some point in the code?
+                            mServerFile.setKeepInSync(mLocalFile.keepInSync());
+                            mServerFile.setLastSyncDateForData(mLocalFile.getLastSyncDateForData());
+                            mServerFile.setStoragePath(mLocalFile.getStoragePath());
+                            mServerFile.setParentId(mLocalFile.getParentId());
+                            mStorageManager.saveFile(mServerFile);
+                            
+                        }
+                        result = new RemoteOperationResult(ResultCode.OK);
+              
+                    } else {
+                        // nothing changed, nothing to do
+                        result = new RemoteOperationResult(ResultCode.OK);
+                    }
+              
+                } 
+          
+            }
+            
+            Log_OC.i(TAG, "Synchronizing " + mAccount.name + ", file " + mLocalFile.getRemotePath() + ": " + result.getLogMessage());
+          
+        } catch (Exception e) {
+            result = new RemoteOperationResult(e);
+            Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", file " + (mLocalFile != null ? mLocalFile.getRemotePath() : "NULL") + ": " + result.getLogMessage(), result.getException());
+
+        } finally {
+            if (propfind != null)
+                propfind.releaseConnection();
+        }
+        return result;
+    }
+
+    
+    /**
+     * Requests for an upload to the FileUploader service
+     * 
+     * @param file     OCFile object representing the file to upload
+     */
+    private void requestForUpload(OCFile file) {
+        Intent i = new Intent(mContext, FileUploader.class);
+        i.putExtra(FileUploader.KEY_ACCOUNT, mAccount);
+        i.putExtra(FileUploader.KEY_FILE, file);
+        /*i.putExtra(FileUploader.KEY_REMOTE_FILE, mRemotePath);    // doing this we would lose the value of keepInSync in the road, and maybe it's not updated in the database when the FileUploader service gets it!  
+        i.putExtra(FileUploader.KEY_LOCAL_FILE, localFile.getStoragePath());*/
+        i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
+        i.putExtra(FileUploader.KEY_FORCE_OVERWRITE, true);
+        mContext.startService(i);
+        mTransferWasRequested = true;
+    }
+
+
+    /**
+     * Requests for a download to the FileDownloader service
+     * 
+     * @param file     OCFile object representing the file to download
+     */
+    private void requestForDownload(OCFile file) {
+        Intent i = new Intent(mContext, FileDownloader.class);
+        i.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount);
+        i.putExtra(FileDownloader.EXTRA_FILE, file);
+        mContext.startService(i);
+        mTransferWasRequested = true;
+    }
+
+
+    /**
+     * Creates and populates a new {@link OCFile} object with the data read from the server.
+     * 
+     * @param we        WebDAV entry read from the server for a WebDAV resource (remote file or folder).
+     * @return          New OCFile instance representing the remote resource described by we.
+     */
+    private OCFile fillOCFile(WebdavEntry we) {
+        OCFile file = new OCFile(we.decodedPath());
+        file.setCreationTimestamp(we.createTimestamp());
+        file.setFileLength(we.contentLength());
+        file.setMimetype(we.contentType());
+        file.setModificationTimestamp(we.modifiedTimestamp());
+        return file;
+    }
+
+
+    public boolean transferWasRequested() {
+        return mTransferWasRequested;
+    }
+
+
+    public OCFile getLocalFile() {
+        return mLocalFile;
+    }
+
+}
diff --git a/src/com/owncloud/android/operations/SynchronizeFolderOperation.java b/src/com/owncloud/android/operations/SynchronizeFolderOperation.java
new file mode 100644 (file)
index 0000000..3d09b47
--- /dev/null
@@ -0,0 +1,365 @@
+/* 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.operations;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import org.apache.http.HttpStatus;
+import org.apache.jackrabbit.webdav.DavConstants;
+import org.apache.jackrabbit.webdav.MultiStatus;
+import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
+
+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;
+import com.owncloud.android.utils.FileStorageUtils;
+
+import android.accounts.Account;
+import android.content.Context;
+
+
+import eu.alefzero.webdav.WebdavClient;
+import eu.alefzero.webdav.WebdavEntry;
+import eu.alefzero.webdav.WebdavUtils;
+
+
+/**
+ * Remote operation performing the synchronization a the contents of a remote folder with the local database
+ * 
+ * @author David A. Velasco
+ */
+public class SynchronizeFolderOperation extends RemoteOperation {
+
+    private static final String TAG = SynchronizeFolderOperation.class.getSimpleName();
+
+    /** Remote folder to synchronize */
+    private String mRemotePath;
+    
+    /** Timestamp for the synchronization in progress */
+    private long mCurrentSyncTime;
+    
+    /** Id of the folder to synchronize in the local database */
+    private long mParentId;
+    
+    /** Access to the local database */
+    private DataStorageManager mStorageManager;
+    
+    /** Account where the file to synchronize belongs */
+    private Account mAccount;
+    
+    /** Android context; necessary to send requests to the download service; maybe something to refactor */
+    private Context mContext;
+    
+    /** Files and folders contained in the synchronized folder */
+    private List<OCFile> mChildren;
+
+    private int mConflictsFound;
+
+    private int mFailsInFavouritesFound;
+
+    private Map<String, String> mForgottenLocalFiles;
+    
+    
+    public SynchronizeFolderOperation(  String remotePath, 
+                                        long currentSyncTime, 
+                                        long parentId, 
+                                        DataStorageManager dataStorageManager, 
+                                        Account account, 
+                                        Context context ) {
+        mRemotePath = remotePath;
+        mCurrentSyncTime = currentSyncTime;
+        mParentId = parentId;
+        mStorageManager = dataStorageManager;
+        mAccount = account;
+        mContext = context;
+        mForgottenLocalFiles = new HashMap<String, String>();
+    }
+    
+    
+    public int getConflictsFound() {
+        return mConflictsFound;
+    }
+    
+    public int getFailsInFavouritesFound() {
+        return mFailsInFavouritesFound;
+    }
+    
+    public Map<String, String> getForgottenLocalFiles() {
+        return mForgottenLocalFiles;
+    }
+    
+    /**
+     * Returns the list of files and folders contained in the synchronized folder, if called after synchronization is complete.
+     * 
+     * @return      List of files and folders contained in the synchronized folder.
+     */
+    public List<OCFile> getChildren() {
+        return mChildren;
+    }
+    
+    
+    @Override
+    protected RemoteOperationResult run(WebdavClient client) {
+        RemoteOperationResult result = null;
+        mFailsInFavouritesFound = 0;
+        mConflictsFound = 0;
+        mForgottenLocalFiles.clear();
+        
+        // code before in FileSyncAdapter.fetchData
+        PropFindMethod query = null;
+        try {
+            Log_OC.d(TAG, "Synchronizing " + mAccount.name + ", fetching files in " + mRemotePath);
+            
+            // remote request 
+            query = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath),
+                    DavConstants.PROPFIND_ALL_PROP,
+                    DavConstants.DEPTH_1);
+            int status = client.executeMethod(query);
+            
+            // check and process response   - /// TODO take into account all the possible status per child-resource
+            if (isMultiStatus(status)) { 
+                MultiStatus resp = query.getResponseBodyAsMultiStatus();
+            
+                // synchronize properties of the parent folder, if necessary
+                if (mParentId == DataStorageManager.ROOT_PARENT_ID) {
+                    WebdavEntry we = new WebdavEntry(resp.getResponses()[0], client.getBaseUri().getPath());
+                    OCFile parent = fillOCFile(we);
+                    mStorageManager.saveFile(parent);
+                    mParentId = parent.getFileId();
+                }
+                
+                // read contents in folder
+                List<OCFile> updatedFiles = new Vector<OCFile>(resp.getResponses().length - 1);
+                List<SynchronizeFileOperation> filesToSyncContents = new Vector<SynchronizeFileOperation>();
+                for (int i = 1; i < resp.getResponses().length; ++i) {
+                    /// new OCFile instance with the data from the server
+                    WebdavEntry we = new WebdavEntry(resp.getResponses()[i], client.getBaseUri().getPath());
+                    OCFile file = fillOCFile(we);
+                    
+                    /// set data about local state, keeping unchanged former data if existing
+                    file.setLastSyncDateForProperties(mCurrentSyncTime);
+                    OCFile oldFile = mStorageManager.getFileByPath(file.getRemotePath());
+                    if (oldFile != null) {
+                        file.setKeepInSync(oldFile.keepInSync());
+                        file.setLastSyncDateForData(oldFile.getLastSyncDateForData());
+                        file.setModificationTimestampAtLastSyncForData(oldFile.getModificationTimestampAtLastSyncForData());    // must be kept unchanged when the file contents are not updated
+                        checkAndFixForeignStoragePath(oldFile);
+                        file.setStoragePath(oldFile.getStoragePath());
+                    }
+
+                    /// scan default location if local copy of file is not linked in OCFile instance
+                    if (file.getStoragePath() == null && !file.isDirectory()) {
+                        File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
+                        if (f.exists()) {
+                            file.setStoragePath(f.getAbsolutePath());
+                            file.setLastSyncDateForData(f.lastModified());
+                        }
+                    }
+                    
+                    /// prepare content synchronization for kept-in-sync files
+                    if (file.keepInSync()) {
+                        SynchronizeFileOperation operation = new SynchronizeFileOperation(  oldFile,        
+                                                                                            file, 
+                                                                                            mStorageManager,
+                                                                                            mAccount,       
+                                                                                            true, 
+                                                                                            false,          
+                                                                                            mContext
+                                                                                            );
+                        filesToSyncContents.add(operation);
+                    }
+                
+                    updatedFiles.add(file);
+                }
+                                
+                // save updated contents in local database; all at once, trying to get a best performance in database update (not a big deal, indeed)
+                mStorageManager.saveFiles(updatedFiles);
+                
+                // request for the synchronization of files AFTER saving last properties
+                SynchronizeFileOperation op = null;
+                RemoteOperationResult contentsResult = null;
+                for (int i=0; i < filesToSyncContents.size(); i++) {
+                    op = filesToSyncContents.get(i);
+                    contentsResult = op.execute(client);   // returns without waiting for upload or download finishes
+                    if (!contentsResult.isSuccess()) {
+                        if (contentsResult.getCode() == ResultCode.SYNC_CONFLICT) {
+                            mConflictsFound++;
+                        } else {
+                            mFailsInFavouritesFound++;
+                            if (contentsResult.getException() != null) {
+                                Log_OC.e(TAG, "Error while synchronizing favourites : " +  contentsResult.getLogMessage(), contentsResult.getException());
+                            } else {
+                                Log_OC.e(TAG, "Error while synchronizing favourites : " + contentsResult.getLogMessage());
+                            }
+                        }
+                    }   // won't let these fails break the synchronization process
+                }
+
+                    
+                // removal of obsolete files
+                mChildren = mStorageManager.getDirectoryContent(mStorageManager.getFileById(mParentId));
+                OCFile file;
+                String currentSavePath = FileStorageUtils.getSavePath(mAccount.name);
+                for (int i=0; i < mChildren.size(); ) {
+                    file = mChildren.get(i);
+                    if (file.getLastSyncDateForProperties() != mCurrentSyncTime) {
+                        Log_OC.d(TAG, "removing file: " + file);
+                        mStorageManager.removeFile(file, (file.isDown() && file.getStoragePath().startsWith(currentSavePath)));
+                        mChildren.remove(i);
+                    } else {
+                        i++;
+                    }
+                }
+                
+            } else {
+                client.exhaustResponse(query.getResponseBodyAsStream());
+            }
+            
+            // prepare result object
+            if (isMultiStatus(status)) {
+                if (mConflictsFound > 0  || mFailsInFavouritesFound > 0) { 
+                    result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);   // should be different result, but will do the job
+                            
+                } else {
+                    result = new RemoteOperationResult(true, status, query.getResponseHeaders());
+                }
+            } else {
+                result = new RemoteOperationResult(false, status, query.getResponseHeaders());
+            }
+            
+            
+            
+        } catch (Exception e) {
+            result = new RemoteOperationResult(e);
+            
+
+        } finally {
+            if (query != null)
+                query.releaseConnection();  // let the connection available for other methods
+            if (result.isSuccess()) {
+                Log_OC.i(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage());
+            } else {
+                if (result.isException()) {
+                    Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage(), result.getException());
+                } else {
+                    Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage());
+                }
+            }
+        }
+        
+        return result;
+    }
+    
+
+    public boolean isMultiStatus(int status) {
+        return (status == HttpStatus.SC_MULTI_STATUS); 
+    }
+
+
+    /**
+     * Creates and populates a new {@link OCFile} object with the data read from the server.
+     * 
+     * @param we        WebDAV entry read from the server for a WebDAV resource (remote file or folder).
+     * @return          New OCFile instance representing the remote resource described by we.
+     */
+    private OCFile fillOCFile(WebdavEntry we) {
+        OCFile file = new OCFile(we.decodedPath());
+        file.setCreationTimestamp(we.createTimestamp());
+        file.setFileLength(we.contentLength());
+        file.setMimetype(we.contentType());
+        file.setModificationTimestamp(we.modifiedTimestamp());
+        file.setParentId(mParentId);
+        return file;
+    }
+    
+
+    /**
+     * Checks the storage path of the OCFile received as parameter. If it's out of the local ownCloud folder,
+     * tries to copy the file inside it. 
+     * 
+     * If the copy fails, the link to the local file is nullified. The account of forgotten files is kept in 
+     * {@link #mForgottenLocalFiles}
+     * 
+     * @param file      File to check and fix.
+     */
+    private void checkAndFixForeignStoragePath(OCFile file) {
+        String storagePath = file.getStoragePath();
+        String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, file);
+        if (storagePath != null && !storagePath.equals(expectedPath)) {
+            /// fix storagePaths out of the local ownCloud folder
+            File originalFile = new File(storagePath);
+            if (FileStorageUtils.getUsableSpace(mAccount.name) < originalFile.length()) {
+                mForgottenLocalFiles.put(file.getRemotePath(), storagePath);
+                file.setStoragePath(null);
+                    
+            } else {
+                InputStream in = null;
+                OutputStream out = null;
+                try {
+                    File expectedFile = new File(expectedPath);
+                    File expectedParent = expectedFile.getParentFile();
+                    expectedParent.mkdirs();
+                    if (!expectedParent.isDirectory()) {
+                        throw new IOException("Unexpected error: parent directory could not be created");
+                    }
+                    expectedFile.createNewFile();
+                    if (!expectedFile.isFile()) {
+                        throw new IOException("Unexpected error: target file could not be created");
+                    }                    
+                    in = new FileInputStream(originalFile);
+                    out = new FileOutputStream(expectedFile);
+                    byte[] buf = new byte[1024];
+                    int len;
+                    while ((len = in.read(buf)) > 0){
+                        out.write(buf, 0, len);
+                    }
+                    file.setStoragePath(expectedPath);
+                    
+                } catch (Exception e) {
+                    Log_OC.e(TAG, "Exception while copying foreign file " + expectedPath, e);
+                    mForgottenLocalFiles.put(file.getRemotePath(), storagePath);
+                    file.setStoragePath(null);
+                    
+                } finally {
+                    try {
+                        if (in != null) in.close();
+                    } catch (Exception 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_OC.d(TAG, "Weird exception while closing output stream for " + expectedPath + " (ignoring)", e);
+                    }
+                }
+            }
+        }
+    }
+
+
+}
diff --git a/src/com/owncloud/android/operations/UpdateOCVersionOperation.java b/src/com/owncloud/android/operations/UpdateOCVersionOperation.java
new file mode 100644 (file)
index 0000000..2e116ac
--- /dev/null
@@ -0,0 +1,109 @@
+/* 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.operations;
+
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.authentication.AccountAuthenticator;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.utils.OwnCloudVersion;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.content.Context;
+
+
+import eu.alefzero.webdav.WebdavClient;
+
+/**
+ * Remote operation that checks the version of an ownCloud server and stores it locally
+ * 
+ * @author David A. Velasco
+ */
+public class UpdateOCVersionOperation extends RemoteOperation {
+
+    private static final String TAG = UpdateOCVersionOperation.class.getSimpleName();
+
+    private Account mAccount;
+    private Context mContext;
+    
+    
+    public UpdateOCVersionOperation(Account account, Context context) {
+        mAccount = account;
+        mContext = context;
+    }
+    
+    
+    @Override
+    protected RemoteOperationResult run(WebdavClient client) {
+        AccountManager accountMngr = AccountManager.get(mContext); 
+        String statUrl = accountMngr.getUserData(mAccount, AccountAuthenticator.KEY_OC_BASE_URL);
+        statUrl += AccountUtils.STATUS_PATH;
+        RemoteOperationResult result = null;
+        GetMethod get = null;
+        try {
+            get = new GetMethod(statUrl);
+            int status = client.executeMethod(get);
+            if (status != HttpStatus.SC_OK) {
+                client.exhaustResponse(get.getResponseBodyAsStream());
+                result = new RemoteOperationResult(false, status, get.getResponseHeaders());
+                
+            } else {
+                String response = get.getResponseBodyAsString();
+                if (response != null) {
+                    JSONObject json = new JSONObject(response);
+                    if (json != null && json.getString("version") != null) {
+                        OwnCloudVersion ocver = new OwnCloudVersion(json.getString("version"));
+                        if (ocver.isVersionValid()) {
+                            accountMngr.setUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION, ocver.toString());
+                            Log_OC.d(TAG, "Got new OC version " + ocver.toString());
+                            result = new RemoteOperationResult(ResultCode.OK);
+                            
+                        } else {
+                            Log_OC.w(TAG, "Invalid version number received from server: " + json.getString("version"));
+                            result = new RemoteOperationResult(RemoteOperationResult.ResultCode.BAD_OC_VERSION);
+                        }
+                    }
+                }
+                if (result == null) {
+                    result = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
+                }
+            }
+            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_OC.e(TAG, "Check for update of ownCloud server version at " + client.getBaseUri() + ": " + result.getLogMessage(), e);
+                
+        } catch (Exception e) {
+            result = new RemoteOperationResult(e);
+            Log_OC.e(TAG, "Check for update of ownCloud server version at " + client.getBaseUri() + ": " + result.getLogMessage(), e);
+            
+        } finally {
+            if (get != null) 
+                get.releaseConnection();
+        }
+        return result;
+    }
+
+}
diff --git a/src/com/owncloud/android/operations/UploadFileOperation.java b/src/com/owncloud/android/operations/UploadFileOperation.java
new file mode 100644 (file)
index 0000000..936ac01
--- /dev/null
@@ -0,0 +1,429 @@
+/* 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.operations;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.HashSet;
+import java.util.Set;
+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 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;
+import com.owncloud.android.utils.FileStorageUtils;
+
+import android.accounts.Account;
+
+
+import eu.alefzero.webdav.FileRequestEntity;
+import eu.alefzero.webdav.OnDatatransferProgressListener;
+import eu.alefzero.webdav.WebdavClient;
+import eu.alefzero.webdav.WebdavUtils;
+
+/**
+ * Remote operation performing the upload of a file to an ownCloud server
+ * 
+ * @author David A. Velasco
+ */
+public class UploadFileOperation extends RemoteOperation {
+
+    private static final String TAG = UploadFileOperation.class.getSimpleName();
+
+    private Account mAccount;
+    private OCFile mFile;
+    private OCFile mOldFile;
+    private String mRemotePath = null;
+    private boolean mIsInstant = false;
+    private boolean mRemoteFolderToBeCreated = false;
+    private boolean mForceOverwrite = false;
+    private int mLocalBehaviour = FileUploader.LOCAL_BEHAVIOUR_COPY;
+    private boolean mWasRenamed = false;
+    private String mOriginalFileName = null;
+    private String mOriginalStoragePath = null;
+    PutMethod mPutMethod = null;
+    private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
+    private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
+
+    protected RequestEntity mEntity = null;
+
+    
+    public UploadFileOperation( Account account,
+                                OCFile file,
+                                boolean isInstant, 
+                                boolean forceOverwrite,
+                                int localBehaviour) {
+        if (account == null)
+            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());
+        }
+
+        mAccount = account;
+        mFile = file;
+        mRemotePath = file.getRemotePath();
+        mIsInstant = isInstant;
+        mForceOverwrite = forceOverwrite;
+        mLocalBehaviour = localBehaviour;
+        mOriginalStoragePath = mFile.getStoragePath();
+        mOriginalFileName = mFile.getFileName();
+    }
+
+    public Account getAccount() {
+        return mAccount;
+    }
+
+    public String getFileName() {
+        return mOriginalFileName;
+    }
+
+    public OCFile getFile() {
+        return mFile;
+    }
+
+    public OCFile getOldFile() {
+        return mOldFile;
+    }
+
+    public String getOriginalStoragePath() {
+        return mOriginalStoragePath;
+    }
+
+    public String getStoragePath() {
+        return mFile.getStoragePath();
+    }
+
+    public String getRemotePath() {
+        return mFile.getRemotePath();
+    }
+
+    public String getMimeType() {
+        return mFile.getMimetype();
+    }
+
+    public boolean isInstant() {
+        return mIsInstant;
+    }
+
+    public boolean isRemoteFolderToBeCreated() {
+        return mRemoteFolderToBeCreated;
+    }
+
+    public void setRemoteFolderToBeCreated() {
+        mRemoteFolderToBeCreated = true;
+    }
+
+    public boolean getForceOverwrite() {
+        return mForceOverwrite;
+    }
+
+    public boolean wasRenamed() {
+        return mWasRenamed;
+    }
+
+    public Set<OnDatatransferProgressListener> getDataTransferListeners() {
+        return mDataTransferListeners;
+    }
+    
+    public void addDatatransferProgressListener (OnDatatransferProgressListener 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
+            if (!mForceOverwrite) {
+                String remotePath = getAvailableRemotePath(client, mRemotePath);
+                mWasRenamed = !remotePath.equals(mRemotePath);
+                if (mWasRenamed) {
+                    createNewOCFile(remotePath);
+                }
+            }
+            nameCheckPassed = true;
+
+            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)
+            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
+
+                } 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
+                        InputStream in = null;
+                        OutputStream out = null;
+                        try {
+                            File temporalParent = temporalFile.getParentFile();
+                            temporalParent.mkdirs();
+                            if (!temporalParent.isDirectory()) {
+                                throw new IOException("Unexpected error: parent directory could not be created");
+                            }
+                            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) {
+                                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();
+                            } catch (Exception e) {
+                                Log_OC.d(TAG, "Weird exception while closing input stream for " + mOriginalStoragePath + " (ignoring)", e);
+                            }
+                            try {
+                                if (out != null)
+                                    out.close();
+                            } catch (Exception e) {
+                                Log_OC.d(TAG, "Weird exception while closing output stream for " + expectedPath + " (ignoring)", e);
+                            }
+                        }
+                    }
+                }
+            }
+            localCopyPassed = true;
+
+            // / perform the upload
+            synchronized (mCancellationRequested) {
+                if (mCancellationRequested.get()) {
+                    throw new OperationCancelledException();
+                } else {
+                    mPutMethod = new PutMethod(client.getBaseUri() + WebdavUtils.encodePath(mFile.getRemotePath()));
+                }
+            }
+            int status = uploadFile(client);
+
+            // / 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
+                        fileToMove = temporalFile;
+                    } else { // FileUploader.LOCAL_BEHAVIOUR_MOVE
+                        fileToMove = originalFile;
+                    }
+                    if (!expectedFile.equals(fileToMove)) {
+                        File expectedFolder = expectedFile.getParentFile();
+                        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 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, (mPutMethod != null ? mPutMethod.getResponseHeaders() : null));
+
+        } catch (Exception e) {
+            // TODO something cleaner with cancellations
+            if (mCancellationRequested.get()) {
+                result = new RemoteOperationResult(new OperationCancelledException());
+            } else {
+                result = new RemoteOperationResult(e);
+            }
+
+        } finally {
+            if (temporalFile != null && !originalFile.equals(temporalFile)) {
+                temporalFile.delete();
+            }
+            if (result.isSuccess()) {
+                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)
+                                + ")";
+                    }
+                    Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage() + complement, result.getException());
+                } else {
+                    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);
+        newFile.setCreationTimestamp(mFile.getCreationTimestamp());
+        newFile.setFileLength(mFile.getFileLength());
+        newFile.setMimetype(mFile.getMimetype());
+        newFile.setModificationTimestamp(mFile.getModificationTimestamp());
+        newFile.setModificationTimestampAtLastSyncForData(mFile.getModificationTimestampAtLastSyncForData());
+        // newFile.setEtag(mFile.getEtag())
+        newFile.setKeepInSync(mFile.keepInSync());
+        newFile.setLastSyncDateForProperties(mFile.getLastSyncDateForProperties());
+        newFile.setLastSyncDateForData(mFile.getLastSyncDateForData());
+        newFile.setStoragePath(mFile.getStoragePath());
+        newFile.setParentId(mFile.getParentId());
+        mOldFile = mFile;
+        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());
+            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
+        }
+        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.
+     * 
+     * @param string
+     * @return
+     */
+    private String getAvailableRemotePath(WebdavClient wc, String remotePath) throws Exception {
+        boolean check = wc.existsFile(remotePath);
+        if (!check) {
+            return remotePath;
+        }
+
+        int pos = remotePath.lastIndexOf(".");
+        String suffix = "";
+        String extension = "";
+        if (pos >= 0) {
+            extension = remotePath.substring(pos + 1);
+            remotePath = remotePath.substring(0, pos);
+        }
+        int count = 2;
+        do {
+            suffix = " (" + count + ")";
+            if (pos >= 0)
+                check = wc.existsFile(remotePath + suffix + "." + extension);
+            else
+                check = wc.existsFile(remotePath + suffix);
+            count++;
+        } while (check);
+
+        if (pos >= 0) {
+            return remotePath + suffix + "." + extension;
+        } else {
+            return remotePath + suffix;
+        }
+    }
+
+    public void cancel() {
+        synchronized (mCancellationRequested) {
+            mCancellationRequested.set(true);
+            if (mPutMethod != null)
+                mPutMethod.abort();
+        }
+    }
+
+}
diff --git a/src/com/owncloud/android/providers/FileContentProvider.java b/src/com/owncloud/android/providers/FileContentProvider.java
new file mode 100644 (file)
index 0000000..7f6a1a4
--- /dev/null
@@ -0,0 +1,302 @@
+/* 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.R;
+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 UriMatcher mUriMatcher;
+//    static {
+//        mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+//        mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, null, 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());
+        
+        String authority = getContext().getResources().getString(R.string.authority);
+        mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+        mUriMatcher.addURI(authority, null, ROOT_DIRECTORY);
+        mUriMatcher.addURI(authority, "file/", SINGLE_FILE);
+        mUriMatcher.addURI(authority, "file/#", SINGLE_FILE);
+        mUriMatcher.addURI(authority, "dir/#", DIRECTORY);
+        
+        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();
+        // DB case_sensitive
+        db.execSQL("PRAGMA case_sensitive_like = true");
+        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);
+        }
+
+    }
+
+}
diff --git a/src/com/owncloud/android/syncadapter/AbstractOwnCloudSyncAdapter.java b/src/com/owncloud/android/syncadapter/AbstractOwnCloudSyncAdapter.java
new file mode 100644 (file)
index 0000000..4773099
--- /dev/null
@@ -0,0 +1,154 @@
+/* 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 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.syncadapter;\r
+\r
+import java.io.IOException;\r
+import java.util.Date;\r
+\r
+import org.apache.http.HttpRequest;\r
+import org.apache.http.HttpResponse;\r
+import org.apache.http.client.ClientProtocolException;\r
+import org.apache.http.conn.ConnectionKeepAliveStrategy;\r
+import org.apache.http.protocol.HttpContext;\r
+\r
+import com.owncloud.android.authentication.AccountUtils;\r
+import com.owncloud.android.authentication.AccountUtils.AccountNotFoundException;\r
+import com.owncloud.android.datamodel.DataStorageManager;\r
+import com.owncloud.android.network.OwnCloudClientUtils;\r
+\r
+\r
+import android.accounts.Account;\r
+import android.accounts.AccountManager;\r
+import android.accounts.AuthenticatorException;\r
+import android.accounts.OperationCanceledException;\r
+import android.content.AbstractThreadedSyncAdapter;\r
+import android.content.ContentProviderClient;\r
+import android.content.Context;\r
+import eu.alefzero.webdav.WebdavClient;\r
+\r
+/**\r
+ * Base SyncAdapter for OwnCloud Designed to be subclassed for the concrete\r
+ * SyncAdapter, like ConcatsSync, CalendarSync, FileSync etc..\r
+ * \r
+ * @author sassman\r
+ * \r
+ */\r
+public abstract class AbstractOwnCloudSyncAdapter extends\r
+        AbstractThreadedSyncAdapter {\r
+\r
+    private AccountManager accountManager;\r
+    private Account account;\r
+    private ContentProviderClient contentProvider;\r
+    private Date lastUpdated;\r
+    private DataStorageManager mStoreManager;\r
+\r
+    private WebdavClient mClient = null;\r
+\r
+    public AbstractOwnCloudSyncAdapter(Context context, boolean autoInitialize) {\r
+        super(context, autoInitialize);\r
+        this.setAccountManager(AccountManager.get(context));\r
+    }\r
+\r
+    public AccountManager getAccountManager() {\r
+        return accountManager;\r
+    }\r
+\r
+    public void setAccountManager(AccountManager accountManager) {\r
+        this.accountManager = accountManager;\r
+    }\r
+\r
+    public Account getAccount() {\r
+        return account;\r
+    }\r
+\r
+    public void setAccount(Account account) {\r
+        this.account = account;\r
+    }\r
+\r
+    public ContentProviderClient getContentProvider() {\r
+        return contentProvider;\r
+    }\r
+\r
+    public void setContentProvider(ContentProviderClient contentProvider) {\r
+        this.contentProvider = contentProvider;\r
+    }\r
+\r
+    public Date getLastUpdated() {\r
+        return lastUpdated;\r
+    }\r
+\r
+    public void setLastUpdated(Date lastUpdated) {\r
+        this.lastUpdated = lastUpdated;\r
+    }\r
+\r
+    public void setStorageManager(DataStorageManager storage_manager) {\r
+        mStoreManager = storage_manager;\r
+    }\r
+\r
+    public DataStorageManager getStorageManager() {\r
+        return mStoreManager;\r
+    }\r
+\r
+    protected ConnectionKeepAliveStrategy getKeepAliveStrategy() {\r
+        return new ConnectionKeepAliveStrategy() {\r
+            public long getKeepAliveDuration(HttpResponse response,\r
+                    HttpContext context) {\r
+                // Change keep alive straategy basing on response: ie\r
+                // forbidden/not found/etc\r
+                // should have keep alive 0\r
+                // default return: 5s\r
+                int statusCode = response.getStatusLine().getStatusCode();\r
+\r
+                // HTTP 400, 500 Errors as well as HTTP 118 - Connection timed\r
+                // out\r
+                if ((statusCode >= 400 && statusCode <= 418)\r
+                        || (statusCode >= 421 && statusCode <= 426)\r
+                        || (statusCode >= 500 && statusCode <= 510)\r
+                        || statusCode == 118) {\r
+                    return 0;\r
+                }\r
+\r
+                return 5 * 1000;\r
+            }\r
+        };\r
+    }\r
+\r
+    protected HttpResponse fireRawRequest(HttpRequest query)\r
+            throws ClientProtocolException, OperationCanceledException,\r
+            AuthenticatorException, IOException {\r
+        /*\r
+         * BasicHttpContext httpContext = new BasicHttpContext(); BasicScheme\r
+         * basicAuth = new BasicScheme();\r
+         * httpContext.setAttribute("preemptive-auth", basicAuth);\r
+         * \r
+         * HttpResponse response = getClient().execute(mHost, query,\r
+         * httpContext);\r
+         */\r
+        return null;\r
+    }\r
+\r
+    protected void initClientForCurrentAccount() throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException {\r
+        AccountUtils.constructFullURLForAccount(getContext(), account);\r
+        mClient = OwnCloudClientUtils.createOwnCloudClient(account, getContext());\r
+    }\r
+    \r
+    protected WebdavClient getClient() {\r
+        return mClient;\r
+    }\r
+}
\ No newline at end of file
diff --git a/src/com/owncloud/android/syncadapter/ContactSyncAdapter.java b/src/com/owncloud/android/syncadapter/ContactSyncAdapter.java
new file mode 100644 (file)
index 0000000..b07c048
--- /dev/null
@@ -0,0 +1,123 @@
+/* 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.syncadapter;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.entity.ByteArrayEntity;
+
+import com.owncloud.android.authentication.AccountAuthenticator;
+import com.owncloud.android.authentication.AccountUtils;
+
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.content.ContentProviderClient;
+import android.content.Context;
+import android.content.SyncResult;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+
+public class ContactSyncAdapter extends AbstractOwnCloudSyncAdapter {
+    private String mAddrBookUri;
+
+    public ContactSyncAdapter(Context context, boolean autoInitialize) {
+        super(context, autoInitialize);
+        mAddrBookUri = null;
+    }
+
+    @Override
+    public void onPerformSync(Account account, Bundle extras, String authority,
+            ContentProviderClient provider, SyncResult syncResult) {
+        setAccount(account);
+        setContentProvider(provider);
+        Cursor c = getLocalContacts(false);
+        if (c.moveToFirst()) {
+            do {
+                String lookup = c.getString(c
+                        .getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
+                String a = getAddressBookUri();
+                String uri = a + lookup + ".vcf";
+                FileInputStream f;
+                try {
+                    f = getContactVcard(lookup);
+                    HttpPut query = new HttpPut(uri);
+                    byte[] b = new byte[f.available()];
+                    f.read(b);
+                    query.setEntity(new ByteArrayEntity(b));
+                    fireRawRequest(query);
+                } catch (IOException e) {
+                    e.printStackTrace();
+                    return;
+                } catch (OperationCanceledException e) {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+                } catch (AuthenticatorException e) {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+                }
+            } while (c.moveToNext());
+            // } while (c.moveToNext());
+        }
+
+    }
+
+    private String getAddressBookUri() {
+        if (mAddrBookUri != null)
+            return mAddrBookUri;
+
+        AccountManager am = getAccountManager();
+        @SuppressWarnings("deprecation")
+        String uri = am.getUserData(getAccount(),
+                AccountAuthenticator.KEY_OC_URL).replace(
+                AccountUtils.WEBDAV_PATH_2_0, AccountUtils.CARDDAV_PATH_2_0);
+        uri += "/addressbooks/"
+                + getAccount().name.substring(0,
+                        getAccount().name.lastIndexOf('@')) + "/default/";
+        mAddrBookUri = uri;
+        return uri;
+    }
+
+    private FileInputStream getContactVcard(String lookupKey)
+            throws IOException {
+        Uri uri = Uri.withAppendedPath(
+                ContactsContract.Contacts.CONTENT_VCARD_URI, lookupKey);
+        AssetFileDescriptor fd = getContext().getContentResolver()
+                .openAssetFileDescriptor(uri, "r");
+        return fd.createInputStream();
+    }
+
+    private Cursor getLocalContacts(boolean include_hidden_contacts) {
+        return getContext().getContentResolver().query(
+                ContactsContract.Contacts.CONTENT_URI,
+                new String[] { ContactsContract.Contacts._ID,
+                        ContactsContract.Contacts.LOOKUP_KEY },
+                ContactsContract.Contacts.IN_VISIBLE_GROUP + " = ?",
+                new String[] { (include_hidden_contacts ? "0" : "1") },
+                ContactsContract.Contacts._ID + " DESC");
+    }
+
+}
diff --git a/src/com/owncloud/android/syncadapter/ContactSyncService.java b/src/com/owncloud/android/syncadapter/ContactSyncService.java
new file mode 100644 (file)
index 0000000..6d7c46c
--- /dev/null
@@ -0,0 +1,44 @@
+/* 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.syncadapter;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+public class ContactSyncService extends Service {
+    private static final Object syncAdapterLock = new Object();
+    private static AbstractOwnCloudSyncAdapter mSyncAdapter = null;
+
+    @Override
+    public void onCreate() {
+        synchronized (syncAdapterLock) {
+            if (mSyncAdapter == null) {
+                mSyncAdapter = new ContactSyncAdapter(getApplicationContext(),
+                        true);
+            }
+        }
+    }
+
+    @Override
+    public IBinder onBind(Intent arg0) {
+        return mSyncAdapter.getSyncAdapterBinder();
+    }
+
+}
diff --git a/src/com/owncloud/android/syncadapter/FileSyncAdapter.java b/src/com/owncloud/android/syncadapter/FileSyncAdapter.java
new file mode 100644 (file)
index 0000000..8c187cc
--- /dev/null
@@ -0,0 +1,410 @@
+/* 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.MainApp;
+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!
+            
+            sendStickyBroadcast(true, remotePath, null);
+            
+        } else {
+            if (result.getCode() == RemoteOperationResult.ResultCode.UNAUTHORIZED ||
+                   // (result.isTemporalRedirection() && result.isIdPRedirection() &&
+                    ( result.isIdPRedirection() && 
+                            MainApp.getAuthTokenTypeSamlSessionCookie().equals(getClient().getAuthTokenType()))) {
+                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());
+                
+                // Update folder size on DB
+                getStorageManager().calculateFolderSize(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) {
+        FileSyncService fileSyncService = new FileSyncService();
+        
+        Intent i = new Intent(fileSyncService.getSyncMessage());
+        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 ||
+                                                // (mLastFailedResult.isTemporalRedirection() && mLastFailedResult.isIdPRedirection() && 
+                                                ( mLastFailedResult.isIdPRedirection() && 
+                                                 MainApp.getAuthTokenTypeSamlSessionCookie().equals(getClient().getAuthTokenType()))
+                                             )
+                                           );
+        // 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_ENFORCED_UPDATE, true);
+            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);
+        
+    }
+    
+    
+}
diff --git a/src/com/owncloud/android/syncadapter/FileSyncService.java b/src/com/owncloud/android/syncadapter/FileSyncService.java
new file mode 100644 (file)
index 0000000..d347265
--- /dev/null
@@ -0,0 +1,54 @@
+/* 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 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
+package com.owncloud.android.syncadapter;\r
+\r
+import android.app.Service;\r
+import android.content.Intent;\r
+import android.os.IBinder;\r
+\r
+/**\r
+ * Background service for syncing files to our local Database\r
+ * \r
+ * @author Bartek Przybylski\r
+ * \r
+ */\r
+public class FileSyncService extends Service {\r
+    public static final String SYNC_MESSAGE = "ACCOUNT_SYNC";\r
+    public static final String SYNC_FOLDER_REMOTE_PATH = "SYNC_FOLDER_REMOTE_PATH";\r
+    public static final String IN_PROGRESS = "SYNC_IN_PROGRESS";\r
+    public static final String ACCOUNT_NAME = "ACCOUNT_NAME";\r
+    public static final String SYNC_RESULT = "SYNC_RESULT";\r
+\r
+    public String getSyncMessage(){\r
+        return getClass().getName().toString() + SYNC_MESSAGE;\r
+    }\r
+    /*\r
+     * {@inheritDoc}\r
+     */\r
+    @Override\r
+    public void onCreate() {\r
+    }\r
+\r
+    /*\r
+     * {@inheritDoc}\r
+     */\r
+    @Override\r
+    public IBinder onBind(Intent intent) {\r
+       return new FileSyncAdapter(getApplicationContext(), true).getSyncAdapterBinder();\r
+    }\r
+}\r
diff --git a/src/com/owncloud/android/ui/ActionItem.java b/src/com/owncloud/android/ui/ActionItem.java
new file mode 100644 (file)
index 0000000..a65f3ad
--- /dev/null
@@ -0,0 +1,61 @@
+/* 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 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
+package com.owncloud.android.ui;\r
+\r
+import android.graphics.drawable.Drawable;\r
+import android.view.View.OnClickListener;\r
+\r
+/**\r
+ * Represents an Item on the ActionBar.\r
+ * \r
+ * @author Bartek Przybylski\r
+ * \r
+ */\r
+public class ActionItem {\r
+    private Drawable mIcon;\r
+    private String mTitle;\r
+    private OnClickListener mClickListener;\r
+\r
+    public ActionItem() {\r
+    }\r
+\r
+    public void setTitle(String title) {\r
+        mTitle = title;\r
+    }\r
+\r
+    public String getTitle() {\r
+        return mTitle;\r
+    }\r
+\r
+    public void setIcon(Drawable icon) {\r
+        mIcon = icon;\r
+    }\r
+\r
+    public Drawable getIcon() {\r
+        return mIcon;\r
+    }\r
+\r
+    public void setOnClickListener(OnClickListener listener) {\r
+        mClickListener = listener;\r
+    }\r
+\r
+    public OnClickListener getOnClickListerner() {\r
+        return mClickListener;\r
+    }\r
+\r
+}\r
diff --git a/src/com/owncloud/android/ui/CustomButton.java b/src/com/owncloud/android/ui/CustomButton.java
new file mode 100644 (file)
index 0000000..ab4db65
--- /dev/null
@@ -0,0 +1,46 @@
+package com.owncloud.android.ui;
+
+import com.owncloud.android.R;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.Button;
+/**
+ * @author masensio
+ *
+ * Button for customizing the button background
+ */
+
+public class CustomButton extends Button {
+    
+    public CustomButton(Context context) {
+        super(context);
+        
+        boolean customButtons = getResources().getBoolean(R.bool.custom_buttons);
+        if (customButtons)
+        {
+            this.setBackgroundResource(R.drawable.btn_default);
+        }
+    }
+
+    public CustomButton(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        boolean customButtons = getResources().getBoolean(R.bool.custom_buttons);
+        if (customButtons)
+        {
+            this.setBackgroundResource(R.drawable.btn_default);
+        }
+    }
+
+    public CustomButton(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        
+        boolean customButtons = getResources().getBoolean(R.bool.custom_buttons);
+        if (customButtons)
+        {
+            this.setBackgroundResource(R.drawable.btn_default);
+        }
+    }
+
+}
diff --git a/src/com/owncloud/android/ui/CustomPopup.java b/src/com/owncloud/android/ui/CustomPopup.java
new file mode 100644 (file)
index 0000000..fccf56d
--- /dev/null
@@ -0,0 +1,154 @@
+/* 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 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
+package com.owncloud.android.ui;\r
+\r
+import android.content.Context;\r
+import android.graphics.Rect;\r
+import android.graphics.drawable.BitmapDrawable;\r
+import android.graphics.drawable.Drawable;\r
+import android.view.Gravity;\r
+import android.view.LayoutInflater;\r
+import android.view.MotionEvent;\r
+import android.view.View;\r
+import android.view.WindowManager;\r
+import android.view.View.OnTouchListener;\r
+import android.view.ViewGroup.LayoutParams;\r
+import android.widget.PopupWindow;\r
+\r
+/**\r
+ * Represents a custom PopupWindows\r
+ * \r
+ * @author Lorensius. W. T\r
+ * \r
+ */\r
+public class CustomPopup {\r
+    protected final View mAnchor;\r
+    protected final PopupWindow mWindow;\r
+    private View root;\r
+    private Drawable background = null;\r
+    protected final WindowManager mWManager;\r
+\r
+    public CustomPopup(View anchor) {\r
+        mAnchor = anchor;\r
+        mWindow = new PopupWindow(anchor.getContext());\r
+\r
+        mWindow.setTouchInterceptor(new OnTouchListener() {\r
+\r
+            public boolean onTouch(View v, MotionEvent event) {\r
+                if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {\r
+                    CustomPopup.this.dismiss();\r
+                    return true;\r
+                }\r
+                return false;\r
+            }\r
+        });\r
+\r
+        mWManager = (WindowManager) anchor.getContext().getSystemService(\r
+                Context.WINDOW_SERVICE);\r
+        onCreate();\r
+    }\r
+\r
+    public void onCreate() {\r
+    }\r
+\r
+    public void onShow() {\r
+    }\r
+\r
+    public void preShow() {\r
+        if (root == null) {\r
+            throw new IllegalStateException(\r
+                    "setContentView called with a view to display");\r
+        }\r
+\r
+        onShow();\r
+\r
+        if (background == null) {\r
+            mWindow.setBackgroundDrawable(new BitmapDrawable());\r
+        } else {\r
+            mWindow.setBackgroundDrawable(background);\r
+        }\r
+\r
+        mWindow.setWidth(WindowManager.LayoutParams.WRAP_CONTENT);\r
+        mWindow.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);\r
+        mWindow.setTouchable(true);\r
+        mWindow.setFocusable(true);\r
+        mWindow.setOutsideTouchable(true);\r
+\r
+        mWindow.setContentView(root);\r
+    }\r
+\r
+    public void setBackgroundDrawable(Drawable background) {\r
+        this.background = background;\r
+    }\r
+\r
+    public void setContentView(View root) {\r
+        this.root = root;\r
+        mWindow.setContentView(root);\r
+    }\r
+\r
+    public void setContentView(int layoutResId) {\r
+        LayoutInflater inflater = (LayoutInflater) mAnchor.getContext()\r
+                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);\r
+        setContentView(inflater.inflate(layoutResId, null));\r
+    }\r
+\r
+    public void showDropDown() {\r
+        showDropDown(0, 0);\r
+    }\r
+\r
+    public void showDropDown(int x, int y) {\r
+        preShow();\r
+        mWindow.setAnimationStyle(android.R.style.Animation_Dialog);\r
+        mWindow.showAsDropDown(mAnchor, x, y);\r
+    }\r
+\r
+    public void showLikeQuickAction() {\r
+        showLikeQuickAction(0, 0);\r
+    }\r
+\r
+    public void showLikeQuickAction(int x, int y) {\r
+        preShow();\r
+\r
+        mWindow.setAnimationStyle(android.R.style.Animation_Dialog);\r
+        int[] location = new int[2];\r
+        mAnchor.getLocationOnScreen(location);\r
+\r
+        Rect anchorRect = new Rect(location[0], location[1], location[0]\r
+                + mAnchor.getWidth(), location[1] + mAnchor.getHeight());\r
+\r
+        root.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,\r
+                LayoutParams.WRAP_CONTENT));\r
+        root.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);\r
+\r
+        int rootW = root.getWidth(), rootH = root.getHeight();\r
+        int screenW = mWManager.getDefaultDisplay().getWidth();\r
+\r
+        int xpos = ((screenW - rootW) / 2) + x;\r
+        int ypos = anchorRect.top - rootH + y;\r
+\r
+        if (rootH > anchorRect.top) {\r
+            ypos = anchorRect.bottom + y;\r
+        }\r
+        mWindow.showAtLocation(mAnchor, Gravity.NO_GRAVITY, xpos, ypos);\r
+    }\r
+\r
+    public void dismiss() {\r
+        mWindow.dismiss();\r
+    }\r
+\r
+}\r
diff --git a/src/com/owncloud/android/ui/ExtendedListView.java b/src/com/owncloud/android/ui/ExtendedListView.java
new file mode 100644 (file)
index 0000000..9fe885b
--- /dev/null
@@ -0,0 +1,73 @@
+/* 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.ui;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.widget.ListView;
+
+/**
+ * ListView allowing to specify the position of an item that should be centered in the visible area, if possible.
+ * 
+ * The cleanest way I found to overcome the problem due to getHeight() returns 0 until the view is really drawn. 
+ *  
+ * @author David A. Velasco
+ */
+public class ExtendedListView extends ListView {
+
+    private int mPositionToSetAndCenter;
+
+    public ExtendedListView(Context context) {
+        super(context);
+    }
+
+    public ExtendedListView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public ExtendedListView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * 
+     */
+    @Override
+    protected void onDraw (Canvas canvas) {
+        super.onDraw(canvas);
+        if (mPositionToSetAndCenter > 0) {
+            this.setSelectionFromTop(mPositionToSetAndCenter, getHeight() / 2);
+            mPositionToSetAndCenter = 0;
+        }
+    }
+    
+    /**
+     * Public method to set the position of the item that should be centered in the visible area of the view.
+     * 
+     * The position is saved here and checked in onDraw().
+     *  
+     * @param position         Position (in the list of items) of the item to center in the visible area.     
+     */
+    public void setAndCenterSelection(int position) {
+        mPositionToSetAndCenter = position;
+    }
+}
diff --git a/src/com/owncloud/android/ui/QuickAction.java b/src/com/owncloud/android/ui/QuickAction.java
new file mode 100644 (file)
index 0000000..86fe3fe
--- /dev/null
@@ -0,0 +1,306 @@
+/* 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 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
+package com.owncloud.android.ui;\r
+\r
+import android.content.Context;\r
+\r
+import android.graphics.Rect;\r
+import android.graphics.drawable.Drawable;\r
+\r
+import android.widget.ImageView;\r
+import android.widget.TextView;\r
+import android.widget.LinearLayout;\r
+import android.widget.ScrollView;\r
+\r
+import android.view.Gravity;\r
+import android.view.LayoutInflater;\r
+import android.view.View;\r
+import android.view.View.OnClickListener;\r
+import android.view.ViewGroup.LayoutParams;\r
+import android.view.ViewGroup;\r
+\r
+import java.util.ArrayList;\r
+\r
+import com.owncloud.android.R;\r
+\r
+\r
+/**\r
+ * Popup window, shows action list as icon and text like the one in Gallery3D\r
+ * app.\r
+ * \r
+ * @author Lorensius. W. T\r
+ */\r
+public class QuickAction extends CustomPopup {\r
+    private final View root;\r
+    private final ImageView mArrowUp;\r
+    private final ImageView mArrowDown;\r
+    private final LayoutInflater inflater;\r
+    private final Context context;\r
+\r
+    protected static final int ANIM_GROW_FROM_LEFT = 1;\r
+    protected static final int ANIM_GROW_FROM_RIGHT = 2;\r
+    protected static final int ANIM_GROW_FROM_CENTER = 3;\r
+    protected static final int ANIM_REFLECT = 4;\r
+    protected static final int ANIM_AUTO = 5;\r
+\r
+    private int animStyle;\r
+    private ViewGroup mTrack;\r
+    private ScrollView scroller;\r
+    private ArrayList<ActionItem> actionList;\r
+\r
+    /**\r
+     * Constructor\r
+     * \r
+     * @param anchor {@link View} on where the popup window should be displayed\r
+     */\r
+    public QuickAction(View anchor) {\r
+        super(anchor);\r
+\r
+        actionList = new ArrayList<ActionItem>();\r
+        context = anchor.getContext();\r
+        inflater = (LayoutInflater) context\r
+                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);\r
+\r
+        root = (ViewGroup) inflater.inflate(R.layout.popup, null);\r
+\r
+        mArrowDown = (ImageView) root.findViewById(R.id.arrow_down);\r
+        mArrowUp = (ImageView) root.findViewById(R.id.arrow_up);\r
+\r
+        setContentView(root);\r
+\r
+        mTrack = (ViewGroup) root.findViewById(R.id.tracks);\r
+        scroller = (ScrollView) root.findViewById(R.id.scroller);\r
+        animStyle = ANIM_AUTO;\r
+    }\r
+\r
+    /**\r
+     * Set animation style\r
+     * \r
+     * @param animStyle animation style, default is set to ANIM_AUTO\r
+     */\r
+    public void setAnimStyle(int animStyle) {\r
+        this.animStyle = animStyle;\r
+    }\r
+\r
+    /**\r
+     * Add action item\r
+     * \r
+     * @param action {@link ActionItem} object\r
+     */\r
+    public void addActionItem(ActionItem action) {\r
+        actionList.add(action);\r
+    }\r
+\r
+    /**\r
+     * Show popup window. Popup is automatically positioned, on top or bottom of\r
+     * anchor view.\r
+     * \r
+     */\r
+    public void show() {\r
+        preShow();\r
+\r
+        int xPos, yPos;\r
+\r
+        int[] location = new int[2];\r
+\r
+        mAnchor.getLocationOnScreen(location);\r
+\r
+        Rect anchorRect = new Rect(location[0], location[1], location[0]\r
+                + mAnchor.getWidth(), location[1] + mAnchor.getHeight());\r
+\r
+        createActionList();\r
+\r
+        root.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,\r
+                LayoutParams.WRAP_CONTENT));\r
+        root.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);\r
+\r
+        int rootHeight = root.getMeasuredHeight();\r
+        int rootWidth = root.getMeasuredWidth();\r
+\r
+        int screenWidth = mWManager.getDefaultDisplay().getWidth();\r
+        int screenHeight = mWManager.getDefaultDisplay().getHeight();\r
+\r
+        // automatically get X coord of popup (top left)\r
+        if ((anchorRect.left + rootWidth) > screenWidth) {\r
+            xPos = anchorRect.left - (rootWidth - mAnchor.getWidth());\r
+        } else {\r
+            if (mAnchor.getWidth() > rootWidth) {\r
+                xPos = anchorRect.centerX() - (rootWidth / 2);\r
+            } else {\r
+                xPos = anchorRect.left;\r
+            }\r
+        }\r
+\r
+        int dyTop = anchorRect.top;\r
+        int dyBottom = screenHeight - anchorRect.bottom;\r
+\r
+        boolean onTop = (dyTop > dyBottom) ? true : false;\r
+\r
+        if (onTop) {\r
+            if (rootHeight > dyTop) {\r
+                yPos = 15;\r
+                LayoutParams l = scroller.getLayoutParams();\r
+                l.height = dyTop - mAnchor.getHeight();\r
+            } else {\r
+                yPos = anchorRect.top - rootHeight;\r
+            }\r
+        } else {\r
+            yPos = anchorRect.bottom;\r
+\r
+            if (rootHeight > dyBottom) {\r
+                LayoutParams l = scroller.getLayoutParams();\r
+                l.height = dyBottom;\r
+            }\r
+        }\r
+\r
+        showArrow(((onTop) ? R.id.arrow_down : R.id.arrow_up),\r
+                anchorRect.centerX() - xPos);\r
+\r
+        setAnimationStyle(screenWidth, anchorRect.centerX(), onTop);\r
+\r
+        mWindow.showAtLocation(mAnchor, Gravity.NO_GRAVITY, xPos, yPos);\r
+    }\r
+\r
+    /**\r
+     * Set animation style\r
+     * \r
+     * @param screenWidth screen width\r
+     * @param requestedX distance from left edge\r
+     * @param onTop flag to indicate where the popup should be displayed. Set\r
+     *            TRUE if displayed on top of anchor view and vice versa\r
+     */\r
+    private void setAnimationStyle(int screenWidth, int requestedX,\r
+            boolean onTop) {\r
+        int arrowPos = requestedX - mArrowUp.getMeasuredWidth() / 2;\r
+\r
+        switch (animStyle) {\r
+        case ANIM_GROW_FROM_LEFT:\r
+            mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Left\r
+                    : R.style.Animations_PopDownMenu_Left);\r
+            break;\r
+\r
+        case ANIM_GROW_FROM_RIGHT:\r
+            mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Right\r
+                    : R.style.Animations_PopDownMenu_Right);\r
+            break;\r
+\r
+        case ANIM_GROW_FROM_CENTER:\r
+            mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Center\r
+                    : R.style.Animations_PopDownMenu_Center);\r
+            break;\r
+\r
+        case ANIM_REFLECT:\r
+            mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Reflect\r
+                    : R.style.Animations_PopDownMenu_Reflect);\r
+            break;\r
+\r
+        case ANIM_AUTO:\r
+            if (arrowPos <= screenWidth / 4) {\r
+                mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Left\r
+                        : R.style.Animations_PopDownMenu_Left);\r
+            } else if (arrowPos > screenWidth / 4\r
+                    && arrowPos < 3 * (screenWidth / 4)) {\r
+                mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Center\r
+                        : R.style.Animations_PopDownMenu_Center);\r
+            } else {\r
+                mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Right\r
+                        : R.style.Animations_PopDownMenu_Right);\r
+            }\r
+\r
+            break;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Create action list\r
+     */\r
+    private void createActionList() {\r
+        View view;\r
+        String title;\r
+        Drawable icon;\r
+        OnClickListener listener;\r
+\r
+        for (int i = 0; i < actionList.size(); i++) {\r
+            title = actionList.get(i).getTitle();\r
+            icon = actionList.get(i).getIcon();\r
+            listener = actionList.get(i).getOnClickListerner();\r
+\r
+            view = getActionItem(title, icon, listener);\r
+\r
+            view.setFocusable(true);\r
+            view.setClickable(true);\r
+\r
+            mTrack.addView(view);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Get action item {@link View}\r
+     * \r
+     * @param title action item title\r
+     * @param icon {@link Drawable} action item icon\r
+     * @param listener {@link View.OnClickListener} action item listener\r
+     * @return action item {@link View}\r
+     */\r
+    private View getActionItem(String title, Drawable icon,\r
+            OnClickListener listener) {\r
+        LinearLayout container = (LinearLayout) inflater.inflate(\r
+                R.layout.action_item, null);\r
+\r
+        ImageView img = (ImageView) container.findViewById(R.id.icon);\r
+        TextView text = (TextView) container.findViewById(R.id.title);\r
+\r
+        if (icon != null) {\r
+            img.setImageDrawable(icon);\r
+        }\r
+\r
+        if (title != null) {\r
+            text.setText(title);\r
+        }\r
+\r
+        if (listener != null) {\r
+            container.setOnClickListener(listener);\r
+        }\r
+\r
+        return container;\r
+    }\r
+\r
+    /**\r
+     * Show arrow\r
+     * \r
+     * @param whichArrow arrow type resource id\r
+     * @param requestedX distance from left screen\r
+     */\r
+    private void showArrow(int whichArrow, int requestedX) {\r
+        final View showArrow = (whichArrow == R.id.arrow_up) ? mArrowUp\r
+                : mArrowDown;\r
+        final View hideArrow = (whichArrow == R.id.arrow_up) ? mArrowDown\r
+                : mArrowUp;\r
+\r
+        final int arrowWidth = mArrowUp.getMeasuredWidth();\r
+\r
+        showArrow.setVisibility(View.VISIBLE);\r
+\r
+        ViewGroup.MarginLayoutParams param = (ViewGroup.MarginLayoutParams) showArrow\r
+                .getLayoutParams();\r
+\r
+        param.leftMargin = requestedX - arrowWidth / 2;\r
+\r
+        hideArrow.setVisibility(View.INVISIBLE);\r
+    }\r
+}
\ No newline at end of file
diff --git a/src/com/owncloud/android/ui/activity/AccountSelectActivity.java b/src/com/owncloud/android/ui/activity/AccountSelectActivity.java
new file mode 100644 (file)
index 0000000..80e38f7
--- /dev/null
@@ -0,0 +1,274 @@
+/* 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.ui.activity;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
+import android.accounts.AccountManagerFuture;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.ContextMenu;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.widget.CheckedTextView;
+import android.widget.ListView;
+import android.widget.SimpleAdapter;
+import android.widget.TextView;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.SherlockListActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountAuthenticator;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.authentication.AuthenticatorActivity;
+
+
+public class AccountSelectActivity extends SherlockListActivity implements
+        AccountManagerCallback<Boolean> {
+
+    private static final String  TAG = "AccountSelectActivity";
+    
+    private static final String PREVIOUS_ACCOUNT_KEY = "ACCOUNT";
+    
+    private final Handler mHandler = new Handler();
+    private Account mPreviousAccount = null;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (savedInstanceState != null) {
+            mPreviousAccount = savedInstanceState.getParcelable(PREVIOUS_ACCOUNT_KEY);
+        } else {
+            mPreviousAccount = AccountUtils.getCurrentOwnCloudAccount(this);
+        }
+        
+        ActionBar action_bar = getSupportActionBar();
+        action_bar.setDisplayShowTitleEnabled(true);
+        action_bar.setDisplayHomeAsUpEnabled(false);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        populateAccountList();
+    }
+    
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (this.isFinishing()) {
+            Account current = AccountUtils.getCurrentOwnCloudAccount(this);
+            if ((mPreviousAccount == null && current != null) || 
+                (mPreviousAccount != null && !mPreviousAccount.equals(current))) {
+                /// the account set as default changed since this activity was created 
+            
+                // trigger synchronization
+                ContentResolver.cancelSync(null, MainApp.getAuthTokenType());
+                Bundle bundle = new Bundle();
+                bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+                ContentResolver.requestSync(AccountUtils.getCurrentOwnCloudAccount(this), MainApp.getAuthTokenType(), bundle);
+                
+                // restart the main activity
+                Intent i = new Intent(this, FileDisplayActivity.class);
+                i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+                startActivity(i);
+            }
+        }
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        // Show Create Account if Multiaccount is enabled
+        if (getResources().getBoolean(R.bool.multiaccount_support)) {
+            MenuInflater inflater = getSherlock().getMenuInflater();
+            inflater.inflate(R.menu.account_picker, menu);
+        }
+        return true;
+    }
+
+    @Override
+    public void onCreateContextMenu(ContextMenu menu, View v,
+            ContextMenuInfo menuInfo) {
+        getMenuInflater().inflate(R.menu.account_picker_long_click, menu);
+        super.onCreateContextMenu(menu, v, menuInfo);
+    }
+
+    @Override
+    protected void onListItemClick(ListView l, View v, int position, long id) {
+        String accountName = ((TextView) v.findViewById(android.R.id.text1))
+                .getText().toString();
+        AccountUtils.setCurrentOwnCloudAccount(this, accountName);
+        finish();   // immediate exit
+    }
+
+    @Override
+    public boolean onMenuItemSelected(int featureId, MenuItem item) {
+        if (item.getItemId() == R.id.createAccount) {
+            /*Intent intent = new Intent(
+                    android.provider.Settings.ACTION_ADD_ACCOUNT);
+            intent.putExtra("authorities",
+                    new String[] { MainApp.getAuthTokenType() });
+            startActivity(intent);*/
+            AccountManager am = AccountManager.get(getApplicationContext());
+            am.addAccount(MainApp.getAccountType(), 
+                            null,
+                            null, 
+                            null, 
+                            this, 
+                            null,                        
+                            null);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Called when the user clicked on an item into the context menu created at 
+     * {@link #onCreateContextMenu(ContextMenu, View, ContextMenuInfo)} for every
+     * ownCloud {@link Account} , containing 'secondary actions' for them.
+     * 
+     * {@inheritDoc}}
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public boolean onContextItemSelected(android.view.MenuItem item) {
+        AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
+        int index = info.position;
+        HashMap<String, String> map = null;
+        try {
+            map = (HashMap<String, String>) getListAdapter().getItem(index);
+        } catch (ClassCastException e) {
+            Log_OC.wtf(TAG, "getitem(index) from list adapter did not return hashmap, bailing out");
+            return false;
+        }
+        
+        String accountName = map.get("NAME");
+        AccountManager am = (AccountManager) getSystemService(ACCOUNT_SERVICE);
+        Account accounts[] = am.getAccountsByType(MainApp.getAccountType());
+        for (Account a : accounts) {
+            if (a.name.equals(accountName)) {
+                if (item.getItemId() == R.id.change_password) {
+                    Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
+                    updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, a);
+                    updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_TOKEN);
+                    startActivity(updateAccountCredentials);
+                    
+                } else if (item.getItemId() == R.id.delete_account) {
+                    am.removeAccount(a, this, mHandler);
+                }
+            }
+        }
+
+        return true;
+    }
+
+    private void populateAccountList() {
+        AccountManager am = (AccountManager) getSystemService(ACCOUNT_SERVICE);
+        Account accounts[] = am
+                .getAccountsByType(MainApp.getAccountType());
+        if (am.getAccountsByType(MainApp.getAccountType()).length == 0) {
+            // Show create account screen if there isn't any account
+            am.addAccount(MainApp.getAccountType(), 
+                    null,
+                    null, 
+                    null, 
+                    this, 
+                    null,                        
+                    null);
+        }
+        else {
+            LinkedList<HashMap<String, String>> ll = new LinkedList<HashMap<String, String>>();
+            for (Account a : accounts) {
+                HashMap<String, String> h = new HashMap<String, String>();
+                h.put("NAME", a.name);
+                h.put("VER",
+                        "ownCloud version: "
+                                + am.getUserData(a,
+                                        AccountAuthenticator.KEY_OC_VERSION));
+                ll.add(h);
+            }
+
+            setListAdapter(new AccountCheckedSimpleAdepter(this, ll,
+                    android.R.layout.simple_list_item_single_choice,
+                    new String[] { "NAME" }, new int[] { android.R.id.text1 }));
+            registerForContextMenu(getListView());
+        }
+    }
+
+    @Override
+    public void run(AccountManagerFuture<Boolean> future) {
+        if (future.isDone()) {
+            Account a = AccountUtils.getCurrentOwnCloudAccount(this);
+            String accountName = "";
+            if (a == null) {
+                Account[] accounts = AccountManager.get(this)
+                        .getAccountsByType(MainApp.getAccountType());
+                if (accounts.length != 0)
+                    accountName = accounts[0].name;
+                AccountUtils.setCurrentOwnCloudAccount(this, accountName);
+            }
+            populateAccountList();
+        }
+    }
+
+    private class AccountCheckedSimpleAdepter extends SimpleAdapter {
+        private Account mCurrentAccount;
+        private List<? extends Map<String, ?>> mPrivateData;
+
+        public AccountCheckedSimpleAdepter(Context context,
+                List<? extends Map<String, ?>> data, int resource,
+                String[] from, int[] to) {
+            super(context, data, resource, from, to);
+            mCurrentAccount = AccountUtils
+                    .getCurrentOwnCloudAccount(AccountSelectActivity.this);
+            mPrivateData = data;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            View v = super.getView(position, convertView, parent);
+            CheckedTextView ctv = (CheckedTextView) v
+                    .findViewById(android.R.id.text1);
+            if (mPrivateData.get(position).get("NAME")
+                    .equals(mCurrentAccount.name)) {
+                ctv.setChecked(true);
+            }
+            return v;
+        }
+
+    }
+
+}
diff --git a/src/com/owncloud/android/ui/activity/ConflictsResolveActivity.java b/src/com/owncloud/android/ui/activity/ConflictsResolveActivity.java
new file mode 100644 (file)
index 0000000..61ac8f5
--- /dev/null
@@ -0,0 +1,103 @@
+/* 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.ui.activity;
+
+import com.owncloud.android.Log_OC;
+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 com.owncloud.android.ui.dialog.ConflictsResolveDialog;
+import com.owncloud.android.ui.dialog.ConflictsResolveDialog.Decision;
+import com.owncloud.android.ui.dialog.ConflictsResolveDialog.OnConflictDecisionMadeListener;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * Wrapper activity which will be launched if keep-in-sync file will be modified by external
+ * application. 
+ * 
+ * @author Bartek Przybylski
+ * @author David A. Velasco
+ */
+public class ConflictsResolveActivity extends FileActivity implements OnConflictDecisionMadeListener {
+
+    private String TAG = ConflictsResolveActivity.class.getSimpleName();
+    
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    public void ConflictDecisionMade(Decision decision) {
+        Intent i = new Intent(getApplicationContext(), FileUploader.class);
+        
+        switch (decision) {
+            case CANCEL:
+                finish();
+                return;
+            case OVERWRITE:
+                i.putExtra(FileUploader.KEY_FORCE_OVERWRITE, true);
+                break;
+            case KEEP_BOTH:
+                i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);
+                break;
+            default:
+                Log_OC.wtf(TAG, "Unhandled conflict decision " + decision);
+                return;
+        }
+        i.putExtra(FileUploader.KEY_ACCOUNT, getAccount());
+        i.putExtra(FileUploader.KEY_FILE, getFile());
+        i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
+        
+        startService(i);
+        finish();
+    }
+
+    @Override
+    protected void onAccountSet(boolean stateWasRecovered) {
+        if (getAccount() != null) {
+            OCFile file = getFile();
+            if (getFile() == null) {
+                Log_OC.e(TAG, "No conflictive file received");
+                finish();
+            } else {
+                /// Check whether the 'main' OCFile handled by the Activity is contained in the current Account
+                DataStorageManager storageManager = new FileDataStorageManager(getAccount(), getContentResolver());
+                file = storageManager.getFileByPath(file.getRemotePath());   // file = null if not in the current Account
+                if (file != null) {
+                    setFile(file);
+                    ConflictsResolveDialog d = ConflictsResolveDialog.newInstance(file.getRemotePath(), this);
+                    d.showDialog(this);
+                    
+                } else {
+                    // account was changed to a different one - just finish
+                    finish();
+                }
+            }
+            
+        } else {
+            Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
+            finish();
+        }
+        
+    }
+}
diff --git a/src/com/owncloud/android/ui/activity/ErrorsWhileCopyingHandlerActivity.java b/src/com/owncloud/android/ui/activity/ErrorsWhileCopyingHandlerActivity.java
new file mode 100644 (file)
index 0000000..fc9afcd
--- /dev/null
@@ -0,0 +1,277 @@
+/* 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.accounts.Account;
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.DialogFragment;
+import android.text.method.ScrollingMovementMethod;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+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;
+import com.owncloud.android.ui.CustomButton;
+import com.owncloud.android.ui.dialog.IndeterminateProgressDialog;
+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. let them unlinked to the remote
+ * files.
+ * 
+ * Shown when the error notification summarizing the list of errors is clicked by the user.
+ * 
+ * @author David A. Velasco
+ */
+public class ErrorsWhileCopyingHandlerActivity  extends SherlockFragmentActivity implements OnClickListener {
+
+    private static final String TAG = ErrorsWhileCopyingHandlerActivity.class.getSimpleName();
+    
+    public static final String EXTRA_ACCOUNT = ErrorsWhileCopyingHandlerActivity.class.getCanonicalName() + ".EXTRA_ACCOUNT";
+    public static final String EXTRA_LOCAL_PATHS = ErrorsWhileCopyingHandlerActivity.class.getCanonicalName() + ".EXTRA_LOCAL_PATHS";
+    public static final String EXTRA_REMOTE_PATHS = ErrorsWhileCopyingHandlerActivity.class.getCanonicalName() + ".EXTRA_REMOTE_PATHS";
+
+    private static final String WAIT_DIALOG_TAG = "WAIT_DIALOG";
+    
+    protected Account mAccount;
+    protected FileDataStorageManager mStorageManager;
+    protected ArrayList<String> mLocalPaths;
+    protected ArrayList<String> mRemotePaths;
+    protected ArrayAdapter<String> mAdapter;
+    protected Handler mHandler;
+    private DialogFragment mCurrentDialog;
+    
+    /**
+     * {@link}
+     */
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        
+        /// read extra parameters in intent
+        Intent intent = getIntent();
+        mAccount = intent.getParcelableExtra(EXTRA_ACCOUNT);
+        mRemotePaths = intent.getStringArrayListExtra(EXTRA_REMOTE_PATHS);
+        mLocalPaths = intent.getStringArrayListExtra(EXTRA_LOCAL_PATHS);
+        mStorageManager = new FileDataStorageManager(mAccount, getContentResolver());
+        mHandler = new Handler();
+        if (mCurrentDialog != null) {
+            mCurrentDialog.dismiss();
+            mCurrentDialog = null;
+        }
+        
+        /// load generic layout
+        setContentView(R.layout.generic_explanation);
+        
+        /// customize text message
+        TextView textView = (TextView) findViewById(R.id.message);
+        String appName = getString(R.string.app_name);
+        String message = String.format(getString(R.string.sync_foreign_files_forgotten_explanation), appName, appName, appName, appName, mAccount.name);
+        textView.setText(message);
+        textView.setMovementMethod(new ScrollingMovementMethod());
+        
+        /// load the list of local and remote files that failed
+        ListView listView = (ListView) findViewById(R.id.list);
+        if (mLocalPaths != null && mLocalPaths.size() > 0) {
+            mAdapter = new ErrorsWhileCopyingListAdapter();
+            listView.setAdapter(mAdapter);
+        } else {
+            listView.setVisibility(View.GONE);
+            mAdapter = null;
+        }
+        
+        /// customize buttons
+        CustomButton cancelBtn = (CustomButton) findViewById(R.id.cancel);
+        CustomButton okBtn = (CustomButton) findViewById(R.id.ok);
+        
+        okBtn.setText(R.string.foreign_files_move);
+        cancelBtn.setOnClickListener(this);
+        okBtn.setOnClickListener(this);
+    }
+    
+    
+    /**
+     * Customized adapter, showing the local files as main text in two-lines list item and the remote files
+     * as the secondary text. 
+     * 
+     * @author David A. Velasco
+     */
+    public class ErrorsWhileCopyingListAdapter extends ArrayAdapter<String> {
+        
+        ErrorsWhileCopyingListAdapter() {
+            super(ErrorsWhileCopyingHandlerActivity.this, android.R.layout.two_line_list_item, android.R.id.text1, mLocalPaths);
+        }
+
+        @Override
+        public boolean isEnabled(int position) {
+            return false;
+        }
+        
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public View getView (int position, View convertView, ViewGroup parent) {
+            View view = convertView;
+            if (view == null) {
+                LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+                view = vi.inflate(android.R.layout.two_line_list_item, null);
+            }
+            if (view != null)  {
+                String localPath = getItem(position);
+                if (localPath != null) {
+                    TextView text1 = (TextView) view.findViewById(android.R.id.text1);
+                    if (text1 != null) {
+                        text1.setText(String.format(getString(R.string.foreign_files_local_text), localPath));
+                    }
+                }
+                if (mRemotePaths != null && mRemotePaths.size() > 0 && position >= 0 && position < mRemotePaths.size()) {
+                    TextView text2 = (TextView) view.findViewById(android.R.id.text2);
+                    String remotePath = mRemotePaths.get(position);
+                    if (text2 != null && remotePath != null) {
+                        text2.setText(String.format(getString(R.string.foreign_files_remote_text), remotePath));
+                    }
+                }
+            }
+            return view;
+        }
+    }
+
+
+    /**
+     * Listener method to perform the MOVE / CANCEL action available in this activity.
+     * 
+     * @param v     Clicked view (button MOVE or CANCEL)
+     */
+    @Override
+    public void onClick(View v) {
+        if (v.getId() == R.id.ok) {
+            /// perform movement operation in background thread
+            Log_OC.d(TAG, "Clicked MOVE, start movement");
+            new MoveFilesTask().execute();            
+            
+        } else if (v.getId() == R.id.cancel) {
+            /// just finish
+            Log_OC.d(TAG, "Clicked CANCEL, bye");
+            finish();
+            
+        } else {
+            Log_OC.e(TAG, "Clicked phantom button, id: " + v.getId());
+        }
+    }
+
+    
+    /**
+     * Asynchronous task performing the move of all the local files to the ownCloud folder.
+     * 
+     * @author David A. Velasco
+     */
+    private class MoveFilesTask extends AsyncTask<Void, Void, Boolean> {
+
+        /**
+         * Updates the UI before trying the movement
+         */
+        @Override
+        protected void onPreExecute () {
+            /// progress dialog and disable 'Move' button
+            mCurrentDialog = IndeterminateProgressDialog.newInstance(R.string.wait_a_moment, false);
+            mCurrentDialog.show(getSupportFragmentManager(), WAIT_DIALOG_TAG);
+            findViewById(R.id.ok).setEnabled(false);
+        }
+        
+        
+        /**
+         * Performs the movement
+         * 
+         * @return     'False' when the movement of any file fails.
+         */
+        @Override
+        protected Boolean doInBackground(Void... params) {
+            while (!mLocalPaths.isEmpty()) {
+                String currentPath = mLocalPaths.get(0);
+                File currentFile = new File(currentPath);
+                String expectedPath = FileStorageUtils.getSavePath(mAccount.name) + mRemotePaths.get(0);
+                File expectedFile = new File(expectedPath);
+
+                if (expectedFile.equals(currentFile) || currentFile.renameTo(expectedFile)) {
+                    // SUCCESS
+                    OCFile file = mStorageManager.getFileByPath(mRemotePaths.get(0));
+                    file.setStoragePath(expectedPath);
+                    mStorageManager.saveFile(file);
+                    mRemotePaths.remove(0);
+                    mLocalPaths.remove(0);
+                        
+                } else {
+                    // FAIL
+                    return false;   
+                }
+            }
+            return true;
+        }
+
+        /**
+         * Updates the activity UI after the movement of local files is tried.
+         * 
+         * If the movement was successful for all the files, finishes the activity immediately.
+         * 
+         * In other case, the list of remaining files is still available to retry the movement.
+         * 
+         * @param result      'True' when the movement was successful.
+         */
+        @Override
+        protected void onPostExecute(Boolean result) {
+            mAdapter.notifyDataSetChanged();
+            mCurrentDialog.dismiss();
+            mCurrentDialog = null;
+            findViewById(R.id.ok).setEnabled(true);
+            
+            if (result) {
+                // nothing else to do in this activity
+                Toast t = Toast.makeText(ErrorsWhileCopyingHandlerActivity.this, getString(R.string.foreign_files_success), Toast.LENGTH_LONG);
+                t.show();
+                finish();
+                
+            } else {
+                Toast t = Toast.makeText(ErrorsWhileCopyingHandlerActivity.this, getString(R.string.foreign_files_fail), Toast.LENGTH_LONG);
+                t.show();
+            }
+        }
+    }    
+
+}
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..76f2ba4
--- /dev/null
@@ -0,0 +1,57 @@
+/* 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 com.owncloud.android.R;\r
+import com.owncloud.android.ui.CustomButton;\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.TextView;\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
+        CustomButton closeBtn = (CustomButton) findViewById(R.id.failed_uploadactivity_close_button);\r
+        \r
+        closeBtn.setOnClickListener(new OnClickListener() {\r
+            @Override\r
+            public void onClick(View v) {\r
+                finish();\r
+            }\r
+        });\r
+    }\r
+}\r
diff --git a/src/com/owncloud/android/ui/activity/FileActivity.java b/src/com/owncloud/android/ui/activity/FileActivity.java
new file mode 100644 (file)
index 0000000..cddf9b6
--- /dev/null
@@ -0,0 +1,320 @@
+/* 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.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
+import android.accounts.AccountManagerFuture;
+import android.accounts.OperationCanceledException;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.webkit.MimeTypeMap;
+
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.datamodel.OCFile;
+
+
+import eu.alefzero.webdav.WebdavUtils;
+
+/**
+ * Activity with common behaviour for activities handling {@link OCFile}s in ownCloud {@link Account}s .
+ * 
+ * @author David A. Velasco
+ */
+public abstract class FileActivity extends SherlockFragmentActivity {
+
+    public static final String EXTRA_FILE = "com.owncloud.android.ui.activity.FILE";
+    public static final String EXTRA_ACCOUNT = "com.owncloud.android.ui.activity.ACCOUNT";
+    public static final String EXTRA_WAITING_TO_PREVIEW = "com.owncloud.android.ui.activity.WAITING_TO_PREVIEW";
+    
+    public static final String TAG = FileActivity.class.getSimpleName(); 
+    
+    
+    /** OwnCloud {@link Account} where the main {@link OCFile} handled by the activity is located. */
+    private Account mAccount;
+    
+    /** Main {@link OCFile} handled by the activity.*/
+    private OCFile mFile;
+    
+    /** Flag to signal that the activity will is finishing to enforce the creation of an ownCloud {@link Account} */
+    private boolean mRedirectingToSetupAccount = false;
+    
+    /** Flag to signal when the value of mAccount was set */ 
+    private boolean mAccountWasSet;
+    
+    /** Flag to signal when the value of mAccount was restored from a saved state */ 
+    private boolean mAccountWasRestored;
+
+    
+    /**
+     * Loads the ownCloud {@link Account} and main {@link OCFile} to be handled by the instance of 
+     * the {@link FileActivity}.
+     * 
+     * Grants that a valid ownCloud {@link Account} is associated to the instance, or that the user 
+     * is requested to create a new one.
+     */
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Account account;
+        if(savedInstanceState != null) {
+            account = savedInstanceState.getParcelable(FileActivity.EXTRA_ACCOUNT);
+            mFile = savedInstanceState.getParcelable(FileActivity.EXTRA_FILE);
+        } else {
+            account = getIntent().getParcelableExtra(FileActivity.EXTRA_ACCOUNT);
+            mFile = getIntent().getParcelableExtra(FileActivity.EXTRA_FILE);
+        }
+
+        setAccount(account, savedInstanceState != null);
+    }
+
+    
+    /**
+     *  Since ownCloud {@link Account}s can be managed from the system setting menu, 
+     *  the existence of the {@link Account} associated to the instance must be checked 
+     *  every time it is restarted.
+     */
+    @Override
+    protected void onRestart() {
+        super.onRestart();
+        boolean validAccount = (mAccount != null && AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), mAccount.name));
+        if (!validAccount) {
+            swapToDefaultAccount();
+        }
+        
+    }
+
+    
+    @Override 
+    protected void onStart() {
+        super.onStart();
+        if (mAccountWasSet) {
+            onAccountSet(mAccountWasRestored);
+        }
+    }
+    
+    
+    /**
+     *  Sets and validates the ownCloud {@link Account} associated to the Activity. 
+     * 
+     *  If not valid, tries to swap it for other valid and existing ownCloud {@link Account}.
+     *  
+     *  POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}. 
+     * 
+     *  @param account          New {@link Account} to set.
+     *  @param savedAccount     When 'true', account was retrieved from a saved instance state.
+     */
+    private void setAccount(Account account, boolean savedAccount) {
+        Account oldAccount = mAccount;
+        boolean validAccount = (account != null && AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), account.name));
+        if (validAccount) {
+            mAccount = account;
+            mAccountWasSet = true;
+            mAccountWasRestored = (savedAccount || mAccount.equals(oldAccount));
+            
+        } else {
+            swapToDefaultAccount();
+        }
+    }
+
+    
+    /**
+     *  Tries to swap the current ownCloud {@link Account} for other valid and existing. 
+     * 
+     *  If no valid ownCloud {@link Account} exists, the the user is requested 
+     *  to create a new ownCloud {@link Account}.
+     *  
+     *  POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}.
+     *   
+     *  @return     'True' if the checked {@link Account} was valid.
+     */
+    private void swapToDefaultAccount() {
+        // default to the most recently used account
+        Account newAccount  = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext());
+        if (newAccount == null) {
+            /// no account available: force account creation
+            createFirstAccount();
+            mRedirectingToSetupAccount = true;
+            mAccountWasSet = false;
+            mAccountWasRestored = false;
+            
+        } else {
+            mAccountWasSet = true;
+            mAccountWasRestored = (newAccount.equals(mAccount));
+            mAccount = newAccount;
+        }
+    }
+
+
+    /**
+     * Launches the account creation activity. To use when no ownCloud account is available
+     */
+    private void createFirstAccount() {
+        AccountManager am = AccountManager.get(getApplicationContext());
+        am.addAccount(MainApp.getAccountType(), 
+                        null,
+                        null, 
+                        null, 
+                        this, 
+                        new AccountCreationCallback(),                        
+                        null);
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putParcelable(FileActivity.EXTRA_FILE, mFile);
+        outState.putParcelable(FileActivity.EXTRA_ACCOUNT, mAccount);
+    }
+    
+    
+    /**
+     * Getter for the main {@link OCFile} handled by the activity.
+     * 
+     * @return  Main {@link OCFile} handled by the activity.
+     */
+    public OCFile getFile() {
+        return mFile;
+    }
+
+    
+    /**
+     * Setter for the main {@link OCFile} handled by the activity.
+     * 
+     * @param file  Main {@link OCFile} to be handled by the activity.
+     */
+    public void setFile(OCFile file) {
+        mFile = file;
+    }
+
+    
+    /**
+     * Getter for the ownCloud {@link Account} where the main {@link OCFile} handled by the activity is located.
+     * 
+     * @return  OwnCloud {@link Account} where the main {@link OCFile} handled by the activity is located.
+     */
+    public Account getAccount() {
+        return mAccount;
+    }
+
+    
+    /**
+     * @return  'True' when the Activity is finishing to enforce the setup of a new account.
+     */
+    protected boolean isRedirectingToSetupAccount() {
+        return mRedirectingToSetupAccount;
+    }
+    
+    
+    /**
+     * Helper class handling a callback from the {@link AccountManager} after the creation of
+     * a new ownCloud {@link Account} finished, successfully or not.
+     * 
+     * At this moment, only called after the creation of the first account.
+     * 
+     * @author David A. Velasco
+     */
+    public class AccountCreationCallback implements AccountManagerCallback<Bundle> {
+
+        @Override
+        public void run(AccountManagerFuture<Bundle> future) {
+            FileActivity.this.mRedirectingToSetupAccount = false;
+            boolean accountWasSet = false;
+            if (future != null) {
+                try {
+                    Bundle result;
+                    result = future.getResult();
+                    String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
+                    String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
+                    if (AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), name)) {
+                        setAccount(new Account(name, type), false);
+                        accountWasSet = true;
+                    }
+                } catch (OperationCanceledException e) {
+                    Log_OC.d(TAG, "Account creation canceled");
+                    
+                } catch (Exception e) {
+                    Log_OC.e(TAG, "Account creation finished in exception: ", e);
+                }
+                    
+            } else {
+                Log_OC.e(TAG, "Account creation callback with null bundle");
+            }
+            if (!accountWasSet) {
+                moveTaskToBack(true);
+            }
+        }
+        
+    }
+    
+    
+    /**
+     *  Called when the ownCloud {@link Account} associated to the Activity was just updated.
+     * 
+     *  Child classes must grant that state depending on the {@link Account} is updated.
+     */
+    protected abstract void onAccountSet(boolean stateWasRecovered);
+    
+    
+
+    public void openFile(OCFile file) {
+        if (file != null) {
+            String storagePath = file.getStoragePath();
+            String encodedStoragePath = WebdavUtils.encodePath(storagePath);
+            
+            Intent intentForSavedMimeType = new Intent(Intent.ACTION_VIEW);
+            intentForSavedMimeType.setDataAndType(Uri.parse("file://"+ encodedStoragePath), file.getMimetype());
+            intentForSavedMimeType.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+            
+            Intent intentForGuessedMimeType = null;
+            if (storagePath.lastIndexOf('.') >= 0) {
+                String guessedMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1));
+                if (guessedMimeType != null && !guessedMimeType.equals(file.getMimetype())) {
+                    intentForGuessedMimeType = new Intent(Intent.ACTION_VIEW);
+                    intentForGuessedMimeType.setDataAndType(Uri.parse("file://"+ encodedStoragePath), guessedMimeType);
+                    intentForGuessedMimeType.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+                }
+            }
+            
+            Intent chooserIntent = null;
+            if (intentForGuessedMimeType != null) {
+                chooserIntent = Intent.createChooser(intentForGuessedMimeType, getString(R.string.actionbar_open_with));
+            } else {
+                chooserIntent = Intent.createChooser(intentForSavedMimeType, getString(R.string.actionbar_open_with));
+            }
+            
+            startActivity(chooserIntent);
+            
+        } else {
+            Log_OC.wtf(TAG, "Trying to open a NULL OCFile");
+        }
+    }
+    
+}
diff --git a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java
new file mode 100644 (file)
index 0000000..b93321a
--- /dev/null
@@ -0,0 +1,1388 @@
+/* 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.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.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
+import android.util.Log;
+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.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.Window;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
+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.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.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.LoadingDialog;
+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.PreviewMediaFragment;
+import com.owncloud.android.ui.preview.PreviewVideoActivity;
+
+
+/**
+ * Displays, what files the user has available in his ownCloud.
+ * 
+ * @author Bartek Przybylski
+ * @author David A. Velasco
+ */
+
+public class FileDisplayActivity extends FileActivity implements
+OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNavigationListener, OnSslValidatorListener, OnRemoteOperationListener, EditNameDialogListener {
+
+    private ArrayAdapter<String> mDirectories;
+
+    /** Access point to the cached database for the current ownCloud {@link Account} */
+    private DataStorageManager mStorageManager = null;
+
+    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 boolean mDualPane;
+    private View mLeftFragmentContainer;
+    private View mRightFragmentContainer;
+
+    private static final String KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW";
+
+    public static final int DIALOG_SHORT_WAIT = 0;
+    private static final int DIALOG_CHOOSE_UPLOAD_SOURCE = 1;
+    private static final int DIALOG_SSL_VALIDATOR = 2;
+    private static final int DIALOG_CERT_NOT_SAVED = 3;
+    
+    private static final String DIALOG_WAIT_TAG = "DIALOG_WAIT";
+
+    public static final String ACTION_DETAILS = "com.owncloud.android.ui.activity.action.DETAILS";
+
+    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.class.getSimpleName();
+
+    private static final String TAG_LIST_OF_FILES = "LIST_OF_FILES";
+    private static final String TAG_SECOND_FRAGMENT = "SECOND_FRAGMENT";
+
+    private OCFile mWaitingToPreview;
+    private Handler mHandler;
+    
+    private String mDownloadAddedMessage;
+    private String mDownloadFinishMessage;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        Log_OC.d(TAG, "onCreate() start");
+        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+
+        super.onCreate(savedInstanceState); // this calls onAccountChanged() when ownCloud Account is valid
+
+        mHandler = new Handler();
+        
+        FileDownloader downloader = new FileDownloader();
+        mDownloadAddedMessage = downloader.getDownloadAddedMessage();
+        mDownloadFinishMessage= downloader.getDownloadFinishMessage();
+
+        /// bindings to transference services
+        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);
+
+        /// Load of saved instance state
+        if(savedInstanceState != null) {
+            mWaitingToPreview = (OCFile) savedInstanceState.getParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW);
+
+        } else {
+            mWaitingToPreview = null;
+        }
+
+        /// USER INTERFACE
+
+        // Inflate and set the layout view
+        setContentView(R.layout.files);    
+        mDualPane = getResources().getBoolean(R.bool.large_land_layout);
+        mLeftFragmentContainer = findViewById(R.id.left_fragment_container);
+        mRightFragmentContainer = findViewById(R.id.right_fragment_container);
+        if (savedInstanceState == null) {
+            createMinFragments();
+        }
+
+        // Action bar setup
+        mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
+        getSupportActionBar().setHomeButtonEnabled(true);       // mandatory since Android ICS, according to the official documentation
+        setSupportProgressBarIndeterminateVisibility(false);    // always AFTER setContentView(...) ; to work around bug in its implementation
+        
+        
+        
+        Log_OC.d(TAG, "onCreate() end");
+    }
+
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        if (mDownloadConnection != null)
+            unbindService(mDownloadConnection);
+        if (mUploadConnection != null)
+            unbindService(mUploadConnection);
+    }
+
+
+    /**
+     *  Called when the ownCloud {@link Account} associated to the Activity was just updated.
+     */ 
+    @Override
+    protected void onAccountSet(boolean stateWasRecovered) {
+        if (getAccount() != null) {
+            mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver());
+
+            /// Check whether the 'main' OCFile handled by the Activity is contained in the current Account
+            OCFile file = getFile();
+            // get parent from path
+            String parentPath = "";
+            if (file != null) {
+                if (file.isDown() && file.getLastSyncDateForProperties() == 0) {
+                    // upload in progress - right now, files are not inserted in the local cache until the upload is successful
+                    // get parent from path
+                    parentPath = file.getRemotePath().substring(0, file.getRemotePath().lastIndexOf(file.getFileName()));
+                    if (mStorageManager.getFileByPath(parentPath) ==  null)
+                        file = null; // not able to know the directory where the file is uploading
+                } else {
+                    file = mStorageManager.getFileByPath(file.getRemotePath());   // currentDir = null if not in the current Account
+                }
+            }
+            if (file == null) {
+                // fall back to root folder
+                file = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR);  // never returns null
+            }
+            setFile(file);
+            mDirectories.clear();
+            OCFile fileIt = file;
+            while(fileIt != null && fileIt.getFileName() != OCFile.PATH_SEPARATOR) {
+                if (fileIt.isDirectory()) {
+                    mDirectories.add(fileIt.getFileName());
+                }
+                // get parent from path
+                parentPath = fileIt.getRemotePath().substring(0, fileIt.getRemotePath().lastIndexOf(fileIt.getFileName()));
+                fileIt = mStorageManager.getFileByPath(parentPath);
+            }
+            mDirectories.add(OCFile.PATH_SEPARATOR);
+            if (!stateWasRecovered) {
+                Log_OC.e(TAG, "Initializing Fragments in onAccountChanged..");
+                initFragmentsWithFile();
+                
+            } else {
+                updateFragmentsVisibility(!file.isDirectory());
+                updateNavigationElementsInActionBar(file.isDirectory() ? null : file);
+            }
+            
+            
+        } else {
+            Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
+        }
+    }
+
+
+    private void createMinFragments() {
+        OCFileListFragment listOfFiles = new OCFileListFragment();
+        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+        transaction.add(R.id.left_fragment_container, listOfFiles, TAG_LIST_OF_FILES);
+        transaction.commit();
+    }
+
+    private void initFragmentsWithFile() {
+        if (getAccount() != null && getFile() != null) {
+            /// First fragment
+            OCFileListFragment listOfFiles = getListOfFilesFragment(); 
+            if (listOfFiles != null) {
+                listOfFiles.listDirectory(getCurrentDir());   
+            } else {
+                Log.e(TAG, "Still have a chance to lose the initializacion of list fragment >(");
+            }
+            
+            /// Second fragment
+            OCFile file = getFile(); 
+            Fragment secondFragment = chooseInitialSecondFragment(file);
+            if (secondFragment != null) {
+                setSecondFragment(secondFragment);
+                updateFragmentsVisibility(true);
+                updateNavigationElementsInActionBar(file);
+                
+            } else {
+                cleanSecondFragment();
+            }
+
+        } else {
+            Log.wtf(TAG, "initFragments() called with invalid NULLs!");
+            if (getAccount() == null) {
+                Log.wtf(TAG, "\t account is NULL");
+            }
+            if (getFile() == null) {
+                Log.wtf(TAG, "\t file is NULL");
+            }
+        }
+    }
+
+    private Fragment chooseInitialSecondFragment(OCFile file) {
+        Fragment secondFragment = null;
+        if (file != null && !file.isDirectory()) {
+            if (file.isDown() && PreviewMediaFragment.canBePreviewed(file) 
+                    && file.getLastSyncDateForProperties() > 0  // temporal fix
+                    ) {
+                int startPlaybackPosition = getIntent().getIntExtra(PreviewVideoActivity.EXTRA_START_POSITION, 0);
+                boolean autoplay = getIntent().getBooleanExtra(PreviewVideoActivity.EXTRA_AUTOPLAY, true);
+                secondFragment = new PreviewMediaFragment(file, getAccount(), startPlaybackPosition, autoplay);
+
+            } else {
+                secondFragment = new FileDetailFragment(file, getAccount());
+            }
+        }
+        return secondFragment;
+    }
+
+
+    /**
+     * Replaces the second fragment managed by the activity with the received as
+     * a parameter.
+     * 
+     * Assumes never will be more than two fragments managed at the same time. 
+     * 
+     * @param fragment      New second Fragment to set.
+     */
+    private void setSecondFragment(Fragment fragment) {
+        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+        transaction.replace(R.id.right_fragment_container, fragment, TAG_SECOND_FRAGMENT);
+        transaction.commit();
+    }
+
+
+    private void updateFragmentsVisibility(boolean existsSecondFragment) {
+        if (mDualPane) {
+            if (mLeftFragmentContainer.getVisibility() != View.VISIBLE) {
+                mLeftFragmentContainer.setVisibility(View.VISIBLE);
+            }
+            if (mRightFragmentContainer.getVisibility() != View.VISIBLE) {
+                mRightFragmentContainer.setVisibility(View.VISIBLE);
+            }
+
+        } else if (existsSecondFragment) {
+            if (mLeftFragmentContainer.getVisibility() != View.GONE) {
+                mLeftFragmentContainer.setVisibility(View.GONE);
+            }
+            if (mRightFragmentContainer.getVisibility() != View.VISIBLE) {
+                mRightFragmentContainer.setVisibility(View.VISIBLE);
+            }
+
+        } else {
+            if (mLeftFragmentContainer.getVisibility() != View.VISIBLE) {
+                mLeftFragmentContainer.setVisibility(View.VISIBLE);
+            }
+            if (mRightFragmentContainer.getVisibility() != View.GONE) {
+                mRightFragmentContainer.setVisibility(View.GONE);
+            }
+        }
+    }
+
+
+    private OCFileListFragment getListOfFilesFragment() {
+        Fragment listOfFiles = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES);
+        if (listOfFiles != null) {
+            return (OCFileListFragment)listOfFiles;
+        }
+        Log_OC.wtf(TAG, "Access to unexisting list of files fragment!!");
+        return null;
+    }
+
+    protected FileFragment getSecondFragment() {
+        Fragment second = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_SECOND_FRAGMENT);
+        if (second != null) {
+            return (FileFragment)second;
+        }
+        return null;
+    }
+
+    public void cleanSecondFragment() {
+        Fragment second = getSecondFragment();
+        if (second != null) {
+            FragmentTransaction tr = getSupportFragmentManager().beginTransaction();
+            tr.remove(second);
+            tr.commit();
+        }
+        updateFragmentsVisibility(false);
+        updateNavigationElementsInActionBar(null);
+    }
+
+    protected void refeshListOfFilesFragment() {
+        OCFileListFragment fileListFragment = getListOfFilesFragment();
+        if (fileListFragment != null) { 
+            fileListFragment.listDirectory();
+        }
+    }
+
+    protected void refreshSecondFragment(String downloadEvent, String downloadedRemotePath, boolean success) {
+        FileFragment secondFragment = getSecondFragment();
+        boolean waitedPreview = (mWaitingToPreview != null && mWaitingToPreview.getRemotePath().equals(downloadedRemotePath));
+        if (secondFragment != null && secondFragment instanceof FileDetailFragment) {
+            FileDetailFragment detailsFragment = (FileDetailFragment) secondFragment;
+            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(mDownloadAddedMessage)) {
+                // grant that the right panel updates the progress bar
+                detailsFragment.listenForTransferProgress();
+                detailsFragment.updateFileDetails(true, false);
+
+            } else if (downloadEvent.equals(mDownloadFinishMessage)) {
+                //  update the right panel
+                boolean detailsFragmentChanged = false;
+                if (waitedPreview) {
+                    if (success) {
+                        mWaitingToPreview = mStorageManager.getFileById(mWaitingToPreview.getFileId());   // update the file from database, for the local storage path
+                        if (PreviewMediaFragment.canBePreviewed(mWaitingToPreview)) {
+                            startMediaPreview(mWaitingToPreview, 0, true);
+                            detailsFragmentChanged = true;
+                        } else {
+                            openFile(mWaitingToPreview);
+                        }
+                    }
+                    mWaitingToPreview = null;
+                }
+                if (!detailsFragmentChanged) {
+                    detailsFragment.updateFileDetails(false, (success));
+                }
+            }
+        }
+    }
+
+
+    @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: {
+            FileFragment second = getSecondFragment();
+            OCFile currentDir = getCurrentDir();
+            if((currentDir != null && currentDir.getParentId() != 0) || 
+                    (second != null && second.getFile() != null)) {
+                onBackPressed(); 
+            }
+            break;
+        }
+        default:
+            retval = super.onOptionsItemSelected(item);
+        }
+        return retval;
+    }
+
+    private void startSynchronization() {
+        ContentResolver.cancelSync(null, MainApp.getAuthTokenType());   // cancel the current synchronizations of any ownCloud account
+        Bundle bundle = new Bundle();
+        bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+        ContentResolver.requestSync(
+                getAccount(),
+                MainApp.getAuthTokenType(), 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
+     */
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, 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, getAccount());
+            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(TAG, "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(TAG, "Unexpected exception when trying to read the result of Intent.ACTION_GET_CONTENT", e);
+            e.printStackTrace();
+
+        } finally {
+            if (filepath == null) {
+                Log_OC.e(TAG, "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,
+                getAccount());
+        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() {
+        OCFileListFragment listOfFiles = getListOfFilesFragment(); 
+        if (mDualPane || getSecondFragment() == null) {
+            if (listOfFiles != null) {  // should never be null, indeed
+                if (mDirectories.getCount() <= 1) {
+                    finish();
+                    return;
+                }
+                popDirname();
+                listOfFiles.onBrowseUp();
+            }
+        }
+        if (listOfFiles != null) {  // should never be null, indeed
+            setFile(listOfFiles.getCurrentFile());
+        }
+        cleanSecondFragment();
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        // responsibility of restore is preferred in onCreate() before than in onRestoreInstanceState when there are Fragments involved
+        Log_OC.e(TAG, "onSaveInstanceState() start");
+        super.onSaveInstanceState(outState);
+        outState.putParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW, mWaitingToPreview);
+        Log_OC.d(TAG, "onSaveInstanceState() end");
+    }
+    
+
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        Log_OC.e(TAG, "onResume() start");
+
+        FileUploader fileUploader = new FileUploader();
+        FileSyncService fileSyncService = new FileSyncService();
+        
+        // Listen for sync messages
+        IntentFilter syncIntentFilter = new IntentFilter(fileSyncService.getSyncMessage());
+        mSyncBroadcastReceiver = new SyncBroadcastReceiver();
+        registerReceiver(mSyncBroadcastReceiver, syncIntentFilter);
+
+        // Listen for upload messages
+        IntentFilter uploadIntentFilter = new IntentFilter(fileUploader.getUploadFinishMessage());
+        mUploadFinishReceiver = new UploadFinishReceiver();
+        registerReceiver(mUploadFinishReceiver, uploadIntentFilter);
+
+        // Listen for download messages
+        IntentFilter downloadIntentFilter = new IntentFilter(mDownloadAddedMessage);
+        downloadIntentFilter.addAction(mDownloadFinishMessage);
+        mDownloadFinishReceiver = new DownloadFinishReceiver();
+        registerReceiver(mDownloadFinishReceiver, downloadIntentFilter);
+    
+        Log_OC.d(TAG, "onResume() end");
+    }
+
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        Log_OC.e(TAG, "onPause() start");
+        if (mSyncBroadcastReceiver != null) {
+            unregisterReceiver(mSyncBroadcastReceiver);
+            mSyncBroadcastReceiver = null;
+        }
+        if (mUploadFinishReceiver != null) {
+            unregisterReceiver(mUploadFinishReceiver);
+            mUploadFinishReceiver = null;
+        }
+        if (mDownloadFinishReceiver != null) {
+            unregisterReceiver(mDownloadFinishReceiver);
+            mDownloadFinishReceiver = null;
+        }
+
+        Log_OC.d(TAG, "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_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, FileDisplayActivity.this.getAccount());
+                            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) {
+                        Intent action = new Intent(FileDisplayActivity.this, InstantUploadActivity.class);
+                        action.putExtra(FileUploader.KEY_ACCOUNT, FileDisplayActivity.this.getAccount());
+                        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;
+    }
+
+
+    /**
+     * Show loading dialog 
+     */
+    public void showLoadingDialog() {
+        // Construct dialog
+        LoadingDialog loading = new LoadingDialog(getResources().getString(R.string.wait_a_moment));
+        FragmentManager fm = getSupportFragmentManager();
+        FragmentTransaction ft = fm.beginTransaction();
+        loading.show(ft, DIALOG_WAIT_TAG);
+        
+    }
+    
+    /**
+     * Dismiss loading dialog
+     */
+    public void dismissLoadingDialog(){
+        Fragment frag = getSupportFragmentManager().findFragmentByTag(DIALOG_WAIT_TAG);
+      if (frag != null) {
+          LoadingDialog loading = (LoadingDialog) frag;
+            loading.dismiss();
+        }
+    }
+    
+    
+    /**
+     * 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);
+        setFile(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(TAG, "sync of account " + accountName + " is in_progress: " + inProgress);
+
+            if (getAccount() != null && accountName.equals(getAccount().name)
+                    && mStorageManager != null
+                    ) {  
+
+                String synchFolderRemotePath = intent.getStringExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH); 
+
+                boolean fillBlankRoot = false;
+                OCFile currentDir = getCurrentDir();
+                if (currentDir == null) {
+                    currentDir = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR);
+                    fillBlankRoot = (currentDir != null);                   
+                }
+
+                if ((synchFolderRemotePath != null && currentDir != null && (currentDir.getRemotePath().equals(synchFolderRemotePath)))
+                        || fillBlankRoot ) {
+                    if (!fillBlankRoot) 
+                        currentDir = mStorageManager.getFileByPath(synchFolderRemotePath);
+                    OCFileListFragment fileListFragment = getListOfFilesFragment();
+                    if (fileListFragment != null) {
+                        fileListFragment.listDirectory(currentDir);
+                    }
+                    if (getSecondFragment() == null)
+                        setFile(currentDir);
+                }
+                
+                setSupportProgressBarIndeterminateVisibility(inProgress);
+                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 = getAccount() != null && accountName.equals(getAccount().name);
+            OCFile currentDir = getCurrentDir();
+            boolean isDescendant = (currentDir != null) && (uploadedRemotePath != null) && (uploadedRemotePath.startsWith(currentDir.getRemotePath()));
+            if (sameAccount && isDescendant) {
+                refeshListOfFilesFragment();
+            }
+        }
+
+    }
+
+
+    /**
+     * 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) {
+                refeshListOfFilesFragment();
+                refreshSecondFragment(intent.getAction(), downloadedRemotePath, intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false));
+            }
+
+            removeStickyBroadcast(intent);
+        }
+
+        private boolean isDescendant(String downloadedRemotePath) {
+            OCFile currentDir = getCurrentDir();
+            return (currentDir != null && downloadedRemotePath != null && downloadedRemotePath.startsWith(currentDir.getRemotePath()));
+        }
+
+        private boolean isSameAccount(Context context, Intent intent) {
+            String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME);
+            return (accountName != null && getAccount() != null && accountName.equals(getAccount().name));
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public DataStorageManager getStorageManager() {
+        return mStorageManager;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     * 
+     * Updates action bar and second fragment, if in dual pane mode.
+     */
+    @Override
+    public void onBrowsedDownTo(OCFile directory) {
+        pushDirname(directory);
+        cleanSecondFragment();
+    }
+
+    /**
+     * Opens the image gallery showing the image {@link OCFile} received as parameter.
+     * 
+     * @param file                      Image {@link OCFile} to show.
+     */
+    @Override
+    public void startImagePreview(OCFile file) {
+        Intent showDetailsIntent = new Intent(this, PreviewImageActivity.class);
+        showDetailsIntent.putExtra(EXTRA_FILE, file);
+        showDetailsIntent.putExtra(EXTRA_ACCOUNT, getAccount());
+        startActivity(showDetailsIntent);
+    }
+
+    /**
+     * Stars the preview of an already down media {@link OCFile}.
+     * 
+     * @param file                      Media {@link OCFile} to preview.
+     * @param startPlaybackPosition     Media position where the playback will be started, in milliseconds.
+     * @param autoplay                  When 'true', the playback will start without user interactions.
+     */
+    @Override
+    public void startMediaPreview(OCFile file, int startPlaybackPosition, boolean autoplay) {
+        Fragment mediaFragment = new PreviewMediaFragment(file, getAccount(), startPlaybackPosition, autoplay);
+        setSecondFragment(mediaFragment);
+        updateFragmentsVisibility(true);
+        updateNavigationElementsInActionBar(file);
+        setFile(file);
+    }
+
+    /**
+     * Requests the download of the received {@link OCFile} , updates the UI
+     * to monitor the download progress and prepares the activity to preview
+     * or open the file when the download finishes.
+     * 
+     * @param file          {@link OCFile} to download and preview.
+     */
+    @Override
+    public void startDownloadForPreview(OCFile file) {
+        Fragment detailFragment = new FileDetailFragment(file, getAccount());
+        setSecondFragment(detailFragment);
+        mWaitingToPreview = file;
+        requestForDownload();
+        updateFragmentsVisibility(true);
+        updateNavigationElementsInActionBar(file);
+        setFile(file);
+    }
+
+
+    /**
+     * Shows the information of the {@link OCFile} received as a 
+     * parameter in the second fragment.
+     * 
+     * @param file          {@link OCFile} whose details will be shown
+     */
+    @Override
+    public void showDetails(OCFile file) {
+        Fragment detailFragment = new FileDetailFragment(file, getAccount());
+        setSecondFragment(detailFragment);
+        updateFragmentsVisibility(true);
+        updateNavigationElementsInActionBar(file);
+        setFile(file);
+    }
+
+
+    /**
+     * TODO
+     */
+    private void updateNavigationElementsInActionBar(OCFile chosenFile) {
+        ActionBar actionBar = getSupportActionBar(); 
+        if (chosenFile == null || mDualPane) {
+            // only list of files - set for browsing through folders
+            OCFile currentDir = getCurrentDir();
+            actionBar.setDisplayHomeAsUpEnabled(currentDir != null && currentDir.getParentId() != 0);
+            actionBar.setDisplayShowTitleEnabled(false);
+            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
+            actionBar.setListNavigationCallbacks(mDirectories, this);   // assuming mDirectories is updated
+
+        } else {
+            actionBar.setDisplayHomeAsUpEnabled(true);
+            actionBar.setDisplayShowTitleEnabled(true);
+            actionBar.setTitle(chosenFile.getFileName());
+            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onFileStateChanged() {
+        refeshListOfFilesFragment();
+        updateNavigationElementsInActionBar(getSecondFragment().getFile());
+    }
+
+
+    /**
+     * {@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
+            OCFileListFragment listOfFiles = getListOfFilesFragment(); 
+            if (listOfFiles != null) {
+                listOfFiles.listDirectory();
+            }
+            FileFragment secondFragment = getSecondFragment();
+            if (secondFragment != null && secondFragment instanceof FileDetailFragment) {
+                FileDetailFragment detailFragment = (FileDetailFragment)secondFragment;
+                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) {
+        dismissLoadingDialog();
+        if (result.isSuccess()) {
+            Toast msg = Toast.makeText(this, R.string.remove_success_msg, Toast.LENGTH_LONG);
+            msg.show();
+            OCFile removedFile = operation.getFile();
+            getSecondFragment();
+            FileFragment second = getSecondFragment();
+            if (second != null && removedFile.equals(second.getFile())) {
+                cleanSecondFragment();
+            }
+            if (mStorageManager.getFileById(removedFile.getParentId()).equals(getCurrentDir())) {
+                refeshListOfFilesFragment();
+            }
+
+        } 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()) {
+            dismissLoadingDialog();
+            refeshListOfFilesFragment();
+
+        } else {
+            //dismissDialog(DIALOG_SHORT_WAIT);
+            dismissLoadingDialog();
+            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) {
+        dismissLoadingDialog();
+        OCFile renamedFile = operation.getFile();
+        if (result.isSuccess()) {
+            if (mDualPane) {
+                FileFragment details = getSecondFragment();
+                if (details != null && details instanceof FileDetailFragment && renamedFile.equals(details.getFile()) ) {
+                    ((FileDetailFragment) details).updateFileDetails(renamedFile, getAccount());
+                }
+            }
+            if (mStorageManager.getFileById(renamedFile.getParentId()).equals(getCurrentDir())) {
+                refeshListOfFilesFragment();
+            }
+
+        } 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) {
+        dismissLoadingDialog();
+        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, getAccount());
+                startActivity(i);
+
+            } else {
+                Toast msg = Toast.makeText(this, R.string.sync_file_fail_msg, Toast.LENGTH_LONG); 
+                msg.show();
+            }
+
+        } else {
+            if (operation.transferWasRequested()) {
+                refeshListOfFilesFragment();
+                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) {
+        if (mDualPane) {
+            FileFragment details = getSecondFragment();
+            if (details != null && details instanceof FileDetailFragment && file.equals(details.getFile()) ) {
+                if (downloading || uploading) {
+                    ((FileDetailFragment)details).updateFileDetails(file, getAccount());
+                } else {
+                    ((FileDetailFragment)details).updateFileDetails(false, true);
+                }
+            }
+        }
+    }
+
+
+    public void onDismiss(EditNameDialog dialog) {
+        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 = getCurrentDir().getRemotePath();
+
+                // Create directory
+                path += newDirectoryName + OCFile.PATH_SEPARATOR;
+                RemoteOperation operation = new CreateFolderOperation(path, false, mStorageManager);
+                operation.execute(  getAccount(), 
+                        FileDisplayActivity.this, 
+                        FileDisplayActivity.this, 
+                        mHandler,
+                        FileDisplayActivity.this);
+
+                showLoadingDialog();
+            }
+        }
+    }
+
+
+    private void requestForDownload() {
+        Account account = getAccount();
+        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);
+        }
+    }
+
+
+    private OCFile getCurrentDir() {
+        OCFile file = getFile();
+        if (file != null) {
+            if (file.isDirectory()) {
+                return file;
+            } else if (mStorageManager != null) {
+                String parentPath = file.getRemotePath().substring(0, file.getRemotePath().lastIndexOf(file.getFileName()));
+                return mStorageManager.getFileByPath(parentPath);
+            }
+        }
+        return null;
+    }
+
+}
diff --git a/src/com/owncloud/android/ui/activity/GenericExplanationActivity.java b/src/com/owncloud/android/ui/activity/GenericExplanationActivity.java
new file mode 100644 (file)
index 0000000..b971c40
--- /dev/null
@@ -0,0 +1,113 @@
+/* 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 android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.method.ScrollingMovementMethod;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.owncloud.android.R;
+
+
+/**
+ * Activity showing a text message and, optionally, a couple list of single or paired text strings.
+ * 
+ * Added to show explanations for notifications when the user clicks on them, and there no place
+ * better to show them.
+ * 
+ * @author David A. Velasco
+ */
+public class GenericExplanationActivity  extends SherlockFragmentActivity {
+
+    public static final String EXTRA_LIST = GenericExplanationActivity.class.getCanonicalName() + ".EXTRA_LIST";
+    public static final String EXTRA_LIST_2 = GenericExplanationActivity.class.getCanonicalName() + ".EXTRA_LIST_2";
+    public static final String MESSAGE = GenericExplanationActivity.class.getCanonicalName() + ".MESSAGE";
+    
+    
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        
+        Intent intent = getIntent();
+        String message = intent.getStringExtra(MESSAGE); 
+        ArrayList<String> list = intent.getStringArrayListExtra(EXTRA_LIST);
+        ArrayList<String> list2 = intent.getStringArrayListExtra(EXTRA_LIST_2);
+        
+        setContentView(R.layout.generic_explanation);
+        
+        if (message != null) {
+            TextView textView = (TextView) findViewById(R.id.message);
+            textView.setText(message);
+            textView.setMovementMethod(new ScrollingMovementMethod());
+        }
+        
+        ListView listView = (ListView) findViewById(R.id.list);
+        if (list != null && list.size() > 0) {
+            //ListAdapter adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list);
+            ListAdapter adapter = new ExplanationListAdapterView(this, list, list2);
+            listView.setAdapter(adapter);
+        } else {
+            listView.setVisibility(View.GONE);
+        }
+    }
+    
+    public class ExplanationListAdapterView extends ArrayAdapter<String> {
+        
+        ArrayList<String> mList;
+        ArrayList<String> mList2;
+        
+        ExplanationListAdapterView(Context context, ArrayList<String> list, ArrayList<String> list2) {
+            super(context, android.R.layout.two_line_list_item, android.R.id.text1, list);
+            mList = list;
+            mList2 = list2;
+        }
+
+        @Override
+        public boolean isEnabled(int position) {
+            return false;
+        }
+        
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public View getView (int position, View convertView, ViewGroup parent) {
+            View view = super.getView(position, convertView, parent);
+            if (view != null)  {
+                if (mList2 != null && mList2.size() > 0 && position >= 0 && position < mList2.size()) {
+                    TextView text2 = (TextView) view.findViewById(android.R.id.text2);
+                    if (text2 != null) {
+                        text2.setText(mList2.get(position));
+                    }
+                }
+            }
+            return view;
+        }
+    }
+
+}
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..ce7ef79
--- /dev/null
@@ -0,0 +1,477 @@
+/* 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 com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.db.DbHandler;
+import com.owncloud.android.files.InstantUploadBroadcastReceiver;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.ui.CustomButton;
+import com.owncloud.android.utils.FileStorageUtils;
+
+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;
+
+
+/**
+ * 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);
+
+        CustomButton deleteAllBtn = (CustomButton) findViewById(R.id.failed_upload_delete_all_btn);
+        deleteAllBtn.setOnClickListener(getDeleteListner());
+        CustomButton retryAllBtn = (CustomButton) findViewById(R.id.failed_upload_retry_all_btn);
+        retryAllBtn.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.background_color);
+                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.background_color);
+        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.background_color);
+        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.background_color);
+        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
diff --git a/src/com/owncloud/android/ui/activity/LandingActivity.java b/src/com/owncloud/android/ui/activity/LandingActivity.java
new file mode 100644 (file)
index 0000000..a6ada6c
--- /dev/null
@@ -0,0 +1,158 @@
+/* ownCloud Android client application\r
+ *   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 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
+package com.owncloud.android.ui.activity;\r
+\r
+import com.actionbarsherlock.app.SherlockFragmentActivity;\r
+import com.owncloud.android.MainApp;\r
+import com.owncloud.android.R;\r
+import com.owncloud.android.ui.adapter.LandingScreenAdapter;\r
+\r
+import android.accounts.Account;\r
+import android.accounts.AccountManager;\r
+import android.app.AlertDialog;\r
+import android.app.Dialog;\r
+import android.content.DialogInterface;\r
+import android.content.DialogInterface.OnClickListener;\r
+import android.content.Intent;\r
+import android.os.Bundle;\r
+import android.view.View;\r
+import android.widget.AdapterView;\r
+import android.widget.AdapterView.OnItemClickListener;\r
+import android.widget.GridView;\r
+import android.widget.Toast;\r
+\r
+\r
+/**\r
+ * This activity is used as a landing page when the user first opens this app.\r
+ * \r
+ * @author Lennart Rosam\r
+ * \r
+ */\r
+public class LandingActivity extends SherlockFragmentActivity implements\r
+        OnClickListener, OnItemClickListener {\r
+\r
+    public static final int DIALOG_SETUP_ACCOUNT = 1;\r
+\r
+    @Override\r
+    protected void onCreate(Bundle savedInstanceState) {\r
+        super.onCreate(savedInstanceState);\r
+        setContentView(R.layout.main);\r
+\r
+        // Fill the grid view of the landing screen with icons\r
+        GridView landingScreenItems = (GridView) findViewById(R.id.homeScreenGrid);\r
+        landingScreenItems.setAdapter(new LandingScreenAdapter(this));\r
+        landingScreenItems.setOnItemClickListener(this);\r
+\r
+        // Check, if there are ownCloud accounts\r
+        if (!accountsAreSetup()) {\r
+            showDialog(DIALOG_SETUP_ACCOUNT);\r
+        } else {\r
+            // Start device tracking service\r
+            Intent locationServiceIntent = new Intent();\r
+            locationServiceIntent\r
+                    .setAction("com.owncloud.android.location.LocationLauncher");\r
+            sendBroadcast(locationServiceIntent);\r
+        }\r
+\r
+    }\r
+\r
+    @Override\r
+    protected void onRestart() {\r
+        super.onRestart();\r
+        // Check, if there are ownCloud accounts\r
+        if (!accountsAreSetup()) {\r
+            showDialog(DIALOG_SETUP_ACCOUNT);\r
+        }\r
+    }\r
+\r
+    @Override\r
+    protected void onRestoreInstanceState(Bundle savedInstanceState) {\r
+        super.onRestoreInstanceState(savedInstanceState);\r
+        // Check, if there are ownCloud accounts\r
+        if (!accountsAreSetup()) {\r
+            showDialog(DIALOG_SETUP_ACCOUNT);\r
+        }\r
+    }\r
+\r
+    @Override\r
+    protected Dialog onCreateDialog(int id) {\r
+        Dialog dialog;\r
+        switch (id) {\r
+        case DIALOG_SETUP_ACCOUNT:\r
+            AlertDialog.Builder 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(R.string.common_ok, this);\r
+            builder.setNegativeButton(R.string.common_cancel, this);\r
+            dialog = builder.create();\r
+            break;\r
+        default:\r
+            dialog = null;\r
+        }\r
+\r
+        return dialog;\r
+    }\r
+\r
+    public void onClick(DialogInterface dialog, int which) {\r
+        // In any case - we won't need it anymore\r
+        dialog.dismiss();\r
+        switch (which) {\r
+        case DialogInterface.BUTTON_POSITIVE:\r
+            Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT);\r
+            intent.putExtra("authorities",\r
+                    new String[] { MainApp.getAuthTokenType() });\r
+            startActivity(intent);\r
+            break;\r
+        case DialogInterface.BUTTON_NEGATIVE:\r
+            finish();\r
+        }\r
+\r
+    }\r
+\r
+    @Override\r
+    /**\r
+     * Start an activity based on the selection\r
+     * the user made\r
+     */\r
+    public void onItemClick(AdapterView<?> parent, View view, int position,\r
+            long id) {\r
+        Intent intent;\r
+        intent = (Intent) parent.getAdapter().getItem(position);\r
+        if (intent != null) {\r
+            startActivity(intent);\r
+        } else {\r
+            // TODO: Implement all of this and make this text go away ;-)\r
+            Toast toast = Toast.makeText(this, "Not yet implemented!",\r
+                    Toast.LENGTH_SHORT);\r
+            toast.show();\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Checks, whether or not there are any ownCloud accounts setup.\r
+     * \r
+     * @return true, if there is at least one account.\r
+     */\r
+    private boolean accountsAreSetup() {\r
+        AccountManager accMan = AccountManager.get(this);\r
+        Account[] accounts = accMan\r
+                .getAccountsByType(MainApp.getAccountType());\r
+        return accounts.length > 0;\r
+    }\r
+\r
+}\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..0aa8475
--- /dev/null
@@ -0,0 +1,120 @@
+/* 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.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.CustomButton;
+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);
+        CustomButton deleteHistoryButton = (CustomButton) 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
diff --git a/src/com/owncloud/android/ui/activity/PinCodeActivity.java b/src/com/owncloud/android/ui/activity/PinCodeActivity.java
new file mode 100644 (file)
index 0000000..068461a
--- /dev/null
@@ -0,0 +1,638 @@
+/* ownCloud Android client application
+ *   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 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.Arrays;
+
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.owncloud.android.R;
+import com.owncloud.android.ui.CustomButton;
+
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnFocusChangeListener;
+import android.view.View.OnKeyListener;
+import android.widget.EditText;
+import android.widget.TextView;
+
+public class PinCodeActivity extends SherlockFragmentActivity {
+
+  
+    public final static String EXTRA_ACTIVITY = "com.owncloud.android.ui.activity.PinCodeActivity.ACTIVITY";
+    public final static String EXTRA_NEW_STATE = "com.owncloud.android.ui.activity.PinCodeActivity.NEW_STATE";
+    
+    CustomButton bCancel;
+    TextView mPinHdr;
+    TextView mPinHdrExplanation;
+    EditText mText1;
+    EditText mText2;
+    EditText mText3;
+    EditText mText4;
+    
+    String [] tempText ={"","","",""};
+    
+    String activity;
+    
+    boolean confirmingPinCode = false;
+    boolean pinCodeChecked = false;
+    boolean newPasswordEntered = false;
+    boolean bChange = true; // to control that only one blocks jump
+    int tCounter ; // Count the number of attempts an user could introduce the PIN code
+
+    
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pincodelock); 
+        
+        Intent intent = getIntent();
+        activity = intent.getStringExtra(EXTRA_ACTIVITY);
+     
+        bCancel = (CustomButton) findViewById(R.id.cancel);
+        mPinHdr = (TextView) findViewById(R.id.pinHdr);
+        mPinHdrExplanation = (TextView) findViewById(R.id.pinHdrExpl);
+        mText1 = (EditText) findViewById(R.id.txt1);
+        mText1.requestFocus();
+        getWindow().setSoftInputMode(android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
+        mText2 = (EditText) findViewById(R.id.txt2);
+        mText3 = (EditText) findViewById(R.id.txt3);
+        mText4 = (EditText) findViewById(R.id.txt4);
+        
+        SharedPreferences appPrefs = PreferenceManager
+                .getDefaultSharedPreferences(getApplicationContext());
+        
+        // Not PIN Code defined yet.
+        // In a previous version settings is allow from start
+        if ( (appPrefs.getString("PrefPinCode1", null) == null ) ){
+            setChangePincodeView(true);
+            pinCodeChecked = true; 
+            newPasswordEntered = true;
+            
+        }else{ 
+            
+            if (appPrefs.getBoolean("set_pincode", false)){
+               // pincode activated
+               if (activity.equals("preferences")){
+                // PIN has been activated yet
+                 mPinHdr.setText(R.string.pincode_configure_your_pin);
+                 mPinHdrExplanation.setVisibility(View.VISIBLE);
+                 pinCodeChecked = true ; // No need to check it 
+                 setChangePincodeView(true);
+               }else{
+                // PIN active
+                 bCancel.setVisibility(View.INVISIBLE);
+                 bCancel.setVisibility(View.GONE);
+                 mPinHdr.setText(R.string.pincode_enter_pin_code);
+                 mPinHdrExplanation.setVisibility(View.INVISIBLE);
+                 setChangePincodeView(false);
+              }
+            
+           }else {
+            // pincode removal
+              mPinHdr.setText(R.string.pincode_remove_your_pincode);
+              mPinHdrExplanation.setVisibility(View.INVISIBLE);
+              pinCodeChecked = false;
+              setChangePincodeView(true); 
+           }
+           
+        }
+        setTextListeners();
+        
+        
+    }
+    
+
+     
+    protected void setInitVars(){
+        confirmingPinCode = false;
+        pinCodeChecked = false;
+        newPasswordEntered = false;
+
+    }
+    
+    protected void setInitView(){
+        bCancel.setVisibility(View.INVISIBLE);
+        bCancel.setVisibility(View.GONE);
+        mPinHdr.setText(R.string.pincode_enter_pin_code);
+        mPinHdrExplanation.setVisibility(View.INVISIBLE);
+    }
+    
+   
+    protected void setChangePincodeView(boolean state){
+       
+        if(state){
+        bCancel.setVisibility(View.VISIBLE);
+        bCancel.setOnClickListener(new OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            
+            SharedPreferences.Editor appPrefsE = PreferenceManager
+                    .getDefaultSharedPreferences(getApplicationContext()).edit();
+            
+            SharedPreferences appPrefs = PreferenceManager
+                    .getDefaultSharedPreferences(getApplicationContext());
+            
+            boolean state = appPrefs.getBoolean("set_pincode", false);
+            appPrefsE.putBoolean("set_pincode",!state); 
+            appPrefsE.commit();
+            setInitVars();
+            finish();
+            }
+        });
+        }  
+    
+    }
+    
+    
+    
+    /*
+     *  
+     */
+    protected void setTextListeners(){
+    
+        /*------------------------------------------------
+         *  FIRST BOX
+         -------------------------------------------------*/
+        
+        mText1.addTextChangedListener(new TextWatcher() {
+
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before,
+                    int count) {
+            }
+
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count,
+                    int after) {
+            }
+
+            @Override
+            public void afterTextChanged(Editable s) {
+                if (s.length() > 0) {
+                    if (!confirmingPinCode){
+                       tempText[0] = mText1.getText().toString();
+                       
+                    }
+                    mText2.requestFocus();
+                 }
+            }
+        });
+        
+        
+
+        /*------------------------------------------------
+         *  SECOND BOX 
+         -------------------------------------------------*/
+        mText2.addTextChangedListener(new TextWatcher() {
+
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before,
+                    int count) {
+            }
+
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count,
+                    int after) {
+            }
+
+            @Override
+            public void afterTextChanged(Editable s) {
+                if (s.length() > 0) {
+                    if (!confirmingPinCode){
+                        tempText[1] = mText2.getText().toString();
+                    }
+                    
+                    mText3.requestFocus();
+                }
+            }
+        });
+        mText2.setOnKeyListener(new OnKeyListener() {
+
+            @Override
+            public boolean onKey(View v, int keyCode, KeyEvent event) {
+                if (keyCode == KeyEvent.KEYCODE_DEL && bChange) {
+
+                    mText1.setText("");
+                    mText1.requestFocus();
+                    if (!confirmingPinCode)
+                       tempText[0] = "";
+                    bChange= false;
+                
+                }else if(!bChange){
+                    bChange=true;
+                    
+                }
+                return false;
+            }
+        });        
+        mText2.setOnFocusChangeListener(new OnFocusChangeListener() {
+               
+            @Override
+            public void onFocusChange(View v, boolean hasFocus) {
+                mText2.setCursorVisible(true);
+                if (mText1.getText().toString().equals("")){
+                    mText2.setSelected(false);
+                    mText2.setCursorVisible(false);
+                    mText1.requestFocus(); 
+                    mText1.setSelected(true);
+                    mText1.setSelection(0);
+                }
+                
+            }
+        });
+        
+        
+        /*------------------------------------------------
+         *  THIRD BOX
+         -------------------------------------------------*/
+        mText3.addTextChangedListener(new TextWatcher() {
+
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before,
+                    int count) {
+            }
+
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count,
+                    int after) {
+            }
+
+            @Override
+            public void afterTextChanged(Editable s) {
+                if (s.length() > 0) {
+                    if (!confirmingPinCode){
+                        tempText[2] = mText3.getText().toString();
+                    }
+                    mText4.requestFocus();
+                }
+            }
+        });
+        
+        mText3.setOnKeyListener(new OnKeyListener() {
+
+            @Override
+            public boolean onKey(View v, int keyCode, KeyEvent event) {
+                if (keyCode == KeyEvent.KEYCODE_DEL && bChange) {
+                    mText2.requestFocus();
+                    if (!confirmingPinCode)
+                        tempText[1] = "";
+                    mText2.setText("");
+                    bChange= false;
+                    
+                }else if(!bChange){
+                    bChange=true;                        
+                    
+                }
+                return false;
+            }
+        });
+        
+        mText3.setOnFocusChangeListener(new OnFocusChangeListener() {
+            
+            @Override
+            public void onFocusChange(View v, boolean hasFocus) {
+                mText3.setCursorVisible(true);
+                if (mText1.getText().toString().equals("")){
+                    mText3.setSelected(false);
+                    mText3.setCursorVisible(false);
+                    mText1.requestFocus();
+                    mText1.setSelected(true);
+                    mText1.setSelection(0);
+                }else if (mText2.getText().toString().equals("")){
+                    mText3.setSelected(false);
+                    mText3.setCursorVisible(false);
+                    mText2.requestFocus();
+                    mText2.setSelected(true);
+                    mText2.setSelection(0);
+                }
+                
+            }
+        });
+        
+        /*------------------------------------------------
+         *  FOURTH BOX
+         -------------------------------------------------*/
+        mText4.addTextChangedListener(new TextWatcher() {
+
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before,
+                    int count) {
+            }
+
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count,
+                    int after) {
+            }
+
+            @Override
+            public void afterTextChanged(Editable s) {
+                if (s.length() > 0) {
+                    
+                    if (!confirmingPinCode){
+                       tempText[3] = mText4.getText().toString();
+                    }
+                    mText1.requestFocus();
+
+                    if (!pinCodeChecked){
+                        pinCodeChecked = checkPincode();
+                    }
+                    
+                    if (pinCodeChecked && activity.equals("FileDisplayActivity")){
+                        finish();
+                    } else if (pinCodeChecked){
+                        
+                        Intent intent = getIntent();
+                        String newState = intent.getStringExtra(EXTRA_NEW_STATE);
+                        
+                        if (newState.equals("false")){
+                            SharedPreferences.Editor appPrefs = PreferenceManager
+                                    .getDefaultSharedPreferences(getApplicationContext()).edit();
+                            appPrefs.putBoolean("set_pincode",false);
+                            appPrefs.commit();
+                            
+                            setInitVars();
+                            pinCodeEnd(false);
+                            
+                        }else{
+                        
+                            if (!confirmingPinCode){
+                                pinCodeChangeRequest();
+                             
+                            } else {
+                                confirmPincode();
+                            }
+                        }
+                   
+                        
+                    }    
+                }
+            }
+        });
+
+        
+        
+        mText4.setOnKeyListener(new OnKeyListener() {
+
+            @Override
+            public boolean onKey(View v, int keyCode, KeyEvent event) {
+                if (keyCode == KeyEvent.KEYCODE_DEL && bChange) {
+                    mText3.requestFocus();
+                    if (!confirmingPinCode)
+                        tempText[2]="";
+                    mText3.setText("");
+                    bChange= false;
+                    
+                }else if(!bChange){
+                    bChange=true;    
+                }
+                return false;
+            }
+        });
+        
+       mText4.setOnFocusChangeListener(new OnFocusChangeListener() {
+            
+            @Override
+            public void onFocusChange(View v, boolean hasFocus) {
+                mText4.setCursorVisible(true);
+                
+                if (mText1.getText().toString().equals("")){
+                    mText4.setSelected(false);
+                    mText4.setCursorVisible(false);
+                    mText1.requestFocus();
+                    mText1.setSelected(true);
+                    mText1.setSelection(0);
+                }else if (mText2.getText().toString().equals("")){
+                    mText4.setSelected(false);
+                    mText4.setCursorVisible(false);
+                    mText2.requestFocus();
+                    mText2.setSelected(true);
+                    mText2.setSelection(0);
+                }else if (mText3.getText().toString().equals("")){
+                    mText4.setSelected(false);
+                    mText4.setCursorVisible(false);
+                    mText3.requestFocus();
+                    mText3.setSelected(true);
+                    mText3.setSelection(0);
+                }
+                
+            }
+        });
+        
+        
+        
+    } // end setTextListener
+    
+    
+    protected void pinCodeChangeRequest(){
+    
+        clearBoxes(); 
+        mPinHdr.setText(R.string.pincode_reenter_your_pincode); 
+        mPinHdrExplanation.setVisibility(View.INVISIBLE);        
+        confirmingPinCode =true;
+        
+    }
+    
+    
+    protected boolean checkPincode(){
+        
+        
+        SharedPreferences appPrefs = PreferenceManager
+                .getDefaultSharedPreferences(getApplicationContext());
+        
+       String pText1 = appPrefs.getString("PrefPinCode1", null);
+        String pText2 = appPrefs.getString("PrefPinCode2", null);
+        String pText3 = appPrefs.getString("PrefPinCode3", null);
+        String pText4 = appPrefs.getString("PrefPinCode4", null);
+
+        if ( tempText[0].equals(pText1) && 
+             tempText[1].equals(pText2) &&
+             tempText[2].equals(pText3) &&
+             tempText[3].equals(pText4) ) {
+            
+            return true;
+        
+        
+        }else {
+            Arrays.fill(tempText, null);
+            AlertDialog aDialog = new AlertDialog.Builder(this).create();
+            CharSequence errorSeq = getString(R.string.common_error);
+            aDialog.setTitle(errorSeq);
+            CharSequence cseq = getString(R.string.pincode_wrong);
+            aDialog.setMessage(cseq);
+            CharSequence okSeq = getString(R.string.common_ok);
+            aDialog.setButton(okSeq, new DialogInterface.OnClickListener(){
+
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                   return; 
+                }
+                
+            });
+            aDialog.show();
+            clearBoxes(); 
+            mPinHdr.setText(R.string.pincode_enter_pin_code);
+            mPinHdrExplanation.setVisibility(View.INVISIBLE);
+            newPasswordEntered = true;
+            confirmingPinCode = false;
+            
+        }
+     
+        
+        return false;
+    }
+    
+    protected void confirmPincode(){
+        
+        confirmingPinCode = false;
+        
+        String rText1 = mText1.getText().toString();
+        String rText2 = mText2.getText().toString();
+        String rText3 = mText3.getText().toString();
+        String rText4 = mText4.getText().toString();
+        
+        if ( tempText[0].equals(rText1) && 
+             tempText[1].equals(rText2) &&
+             tempText[2].equals(rText3) &&
+             tempText[3].equals(rText4) ) {
+                        
+            savePincodeAndExit();
+            
+        } else {
+            
+            Arrays.fill(tempText, null);
+            AlertDialog aDialog = new AlertDialog.Builder(this).create();
+            CharSequence errorSeq = getString(R.string.common_error);
+            aDialog.setTitle(errorSeq);
+            CharSequence cseq = getString(R.string.pincode_mismatch);
+            aDialog.setMessage(cseq);
+            CharSequence okSeq = getString(R.string.common_ok);
+            aDialog.setButton(okSeq, new DialogInterface.OnClickListener(){
+
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                   return; 
+                }
+                
+            });
+            aDialog.show();
+            mPinHdr.setText(R.string.pincode_configure_your_pin);
+            mPinHdrExplanation.setVisibility(View.VISIBLE);
+            clearBoxes();
+        }
+    
+    }
+   
+    
+    protected void pinCodeEnd(boolean state){
+        AlertDialog aDialog = new AlertDialog.Builder(this).create();
+        
+        if (state){
+            CharSequence saveSeq = getString(R.string.common_save_exit);
+            aDialog.setTitle(saveSeq);
+            CharSequence cseq = getString(R.string.pincode_stored);
+            aDialog.setMessage(cseq);
+            
+        }else{
+            CharSequence saveSeq = getString(R.string.common_save_exit);
+            aDialog.setTitle(saveSeq);
+            CharSequence cseq = getString(R.string.pincode_removed);
+            aDialog.setMessage(cseq);
+            
+        }
+        CharSequence okSeq = getString(R.string.common_ok);
+        aDialog.setButton(okSeq, new DialogInterface.OnClickListener(){
+
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                finish();
+                return; 
+            }
+            
+        });
+        aDialog.show(); 
+    }
+    
+    protected void savePincodeAndExit(){
+        SharedPreferences.Editor appPrefs = PreferenceManager
+                .getDefaultSharedPreferences(getApplicationContext()).edit();
+        
+        appPrefs.putString("PrefPinCode1", tempText[0]);
+        appPrefs.putString("PrefPinCode2",tempText[1]);
+        appPrefs.putString("PrefPinCode3", tempText[2]);
+        appPrefs.putString("PrefPinCode4", tempText[3]);
+        appPrefs.putBoolean("set_pincode",true);
+        appPrefs.commit();
+        
+        pinCodeEnd(true);
+        
+        
+        
+    }
+    
+    
+    protected void clearBoxes(){
+        
+        mText1.setText("");
+        mText2.setText("");
+        mText3.setText("");
+        mText4.setText("");
+        mText1.requestFocus(); 
+    }
+    
+    
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event){
+        if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount()== 0){
+            
+            if (activity.equals("preferences")){
+                SharedPreferences.Editor appPrefsE = PreferenceManager
+            
+                    .getDefaultSharedPreferences(getApplicationContext()).edit();
+            
+                SharedPreferences appPrefs = PreferenceManager
+                    .getDefaultSharedPreferences(getApplicationContext());
+            
+                boolean state = appPrefs.getBoolean("set_pincode", false);
+                appPrefsE.putBoolean("set_pincode",!state); 
+                appPrefsE.commit();
+                setInitVars();
+                finish();
+            }
+            return true; 
+            
+        }
+        
+        return super.onKeyDown(keyCode, event);
+    }
+    
+   
+
+    
+            
+}
diff --git a/src/com/owncloud/android/ui/activity/Preferences.java b/src/com/owncloud/android/ui/activity/Preferences.java
new file mode 100644 (file)
index 0000000..20c14bc
--- /dev/null
@@ -0,0 +1,359 @@
+/* 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.util.Vector;
+
+import android.accounts.Account;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.Uri;
+import android.os.Bundle;
+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.PreferenceCategory;
+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.authentication.AccountUtils;
+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;
+                }
+            });            
+            
+        }
+        
+        
+
+        PreferenceCategory preferenceCategory = (PreferenceCategory) findPreference("more");
+        
+        boolean helpEnabled = getResources().getBoolean(R.bool.help_enabled);
+        Preference pHelp =  findPreference("help");
+        if (pHelp != null ){
+            if (helpEnabled) {
+                pHelp.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+                    @Override
+                    public boolean onPreferenceClick(Preference preference) {
+                        String helpWeb   =(String) getText(R.string.url_help);
+                        if (helpWeb != null && helpWeb.length() > 0) {
+                            Uri uriUrl = Uri.parse(helpWeb);
+                            Intent intent = new Intent(Intent.ACTION_VIEW, uriUrl);
+                            startActivity(intent);
+                        }
+                        return true;
+                    }
+                });
+            } else {
+                preferenceCategory.removePreference(pHelp);
+            }
+            
+        }
+        
+       
+       boolean recommendEnabled = getResources().getBoolean(R.bool.recommend_enabled);
+       Preference pRecommend =  findPreference("recommend");
+        if (pRecommend != null){
+            if (recommendEnabled) {
+                pRecommend.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+                    @Override
+                    public boolean onPreferenceClick(Preference preference) {
+
+                        Intent intent = new Intent(Intent.ACTION_SENDTO); 
+                        intent.setType("text/plain");
+                        Account currentAccount = AccountUtils.getCurrentOwnCloudAccount(Preferences.this);
+                        String appName = getString(R.string.app_name);
+                        String username = currentAccount.name.substring(0, currentAccount.name.lastIndexOf('@')); 
+                        String recommendSubject = String.format(getString(R.string.recommend_subject), username, appName);
+                        //String recommendSubject = String.format(getString(R.string.recommend_subject), appName);
+                        intent.putExtra(Intent.EXTRA_SUBJECT, recommendSubject);
+                        String recommendText = String.format(getString(R.string.recommend_text), getString(R.string.app_name), username);
+                        //String recommendText = String.format(getString(R.string.recommend_text), getString(R.string.app_name), getString(R.string.url_app_download));
+                        intent.putExtra(Intent.EXTRA_TEXT, recommendText);
+
+                        intent.setData(Uri.parse(getString(R.string.mail_recommend))); 
+                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
+                        startActivity(intent);
+
+
+                        return(true);
+
+                    }
+                });
+            } else {
+                preferenceCategory.removePreference(pRecommend);
+            }
+            
+        }
+        
+        boolean feedbackEnabled = getResources().getBoolean(R.bool.feedback_enabled);
+        Preference pFeedback =  findPreference("feedback");
+        if (pFeedback != null){
+            if (feedbackEnabled) {
+                pFeedback.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+                    @Override
+                    public boolean onPreferenceClick(Preference preference) {
+                        String feedbackMail   =(String) getText(R.string.mail_feedback);
+                        String feedback   =(String) getText(R.string.prefs_feedback);
+                        Intent intent = new Intent(Intent.ACTION_SENDTO); 
+                        intent.setType("text/plain");
+                        intent.putExtra(Intent.EXTRA_SUBJECT, feedback);
+                        
+                        intent.setData(Uri.parse(feedbackMail)); 
+                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
+                        startActivity(intent);
+                        
+                        return true;
+                    }
+                });
+            } else {
+                preferenceCategory.removePreference(pFeedback);
+            }
+            
+        }
+        
+        boolean imprintEnabled = getResources().getBoolean(R.bool.imprint_enabled);
+        Preference pImprint =  findPreference("imprint");
+        if (pImprint != null) {
+            if (imprintEnabled) {
+                pImprint.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+                    @Override
+                    public boolean onPreferenceClick(Preference preference) {
+                        String imprintWeb = (String) getText(R.string.url_imprint);
+                        if (imprintWeb != null && imprintWeb.length() > 0) {
+                            Uri uriUrl = Uri.parse(imprintWeb);
+                            Intent intent = new Intent(Intent.ACTION_VIEW, uriUrl);
+                            startActivity(intent);
+                        }
+                        //ImprintDialog.newInstance(true).show(preference.get, "IMPRINT_DIALOG");
+                        return true;
+                    }
+                });
+            } else {
+                preferenceCategory.removePreference(pImprint);
+            }
+        }
+            
+        /* 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);
+               }
+       }
+       
+       /* DISABLED FOR RELEASE UNTIL FIXED 
+       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;
+    }
+}
diff --git a/src/com/owncloud/android/ui/activity/PreferencesNewSession.java b/src/com/owncloud/android/ui/activity/PreferencesNewSession.java
new file mode 100644 (file)
index 0000000..c43b29f
--- /dev/null
@@ -0,0 +1,116 @@
+/* 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 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.accounts.AccountAuthenticatorActivity;\r
+import android.app.Activity;\r
+import android.os.Bundle;\r
+import android.view.View;\r
+import android.view.View.OnClickListener;\r
+\r
+public class PreferencesNewSession extends AccountAuthenticatorActivity\r
+        implements OnClickListener {\r
+    @Override\r
+    public void onCreate(Bundle savedInstanceState) {\r
+        super.onCreate(savedInstanceState);\r
+        // setContentView(R.layout.add_new_session);\r
+        /*\r
+         * EditText et;// = (EditText)\r
+         * findViewById(R.id.newSession_sessionName);\r
+         * \r
+         * et = (EditText) findViewById(R.id.newSession_URL); if\r
+         * (getIntent().hasExtra("sessionURL")) { try { URI uri = new\r
+         * URI(getIntent().getStringExtra("sessionURL")); String url =\r
+         * uri.getHost(); if (uri.getPort() != -1) { url += ":" +\r
+         * String.valueOf(uri.getPort()); } if (uri.getPath() != null) { url +=\r
+         * uri.getPath(); } else { url += "/"; } et.setText(url); et =\r
+         * (EditText) findViewById(R.id.newSession_username); if\r
+         * (uri.getAuthority() != null) { if (uri.getUserInfo().indexOf(':') !=\r
+         * -1) { et.setText(uri.getUserInfo().substring(0,\r
+         * uri.getUserInfo().indexOf(':'))); et = (EditText)\r
+         * findViewById(R.id.newSession_password);\r
+         * et.setText(uri.getUserInfo().substring\r
+         * (uri.getUserInfo().indexOf(':')+1)); } else {\r
+         * et.setText(uri.getUserInfo()); } }\r
+         * \r
+         * } catch (URISyntaxException e) { Log.e(TAG, "Incorrect URI syntax " +\r
+         * e.getLocalizedMessage()); } }\r
+         * \r
+         * mReturnData = new Intent(); setResult(Activity.RESULT_OK,\r
+         * mReturnData); ((Button)\r
+         * findViewById(R.id.button1)).setOnClickListener(this); ((Button)\r
+         * findViewById(R.id.button2)).setOnClickListener(this);\r
+         */\r
+    }\r
+\r
+    @Override\r
+    protected void onResume() {\r
+        super.onResume();\r
+    }\r
+\r
+    public void onClick(View v) {\r
+        /*\r
+         * switch (v.getId()) { case R.id.button1: Intent intent = new Intent();\r
+         * if (getIntent().hasExtra("sessionId")) { intent.putExtra("sessionId",\r
+         * getIntent().getIntExtra("sessionId", -1)); } //String sessionName =\r
+         * ((EditText)\r
+         * findViewById(R.id.newSession_sessionName)).getText().toString(); //\r
+         * if (sessionName.trim().equals("") || !isNameValid(sessionName)) { //\r
+         * Toast.makeText(this, R.string.new_session_session_name_error,\r
+         * Toast.LENGTH_LONG).show(); // break; // } URI uri = prepareURI(); if\r
+         * (uri != null) { //intent.putExtra("sessionName", sessionName);\r
+         * intent.putExtra("sessionURL", uri.toString());\r
+         * setResult(Activity.RESULT_OK, intent); AccountManager accMgr =\r
+         * AccountManager.get(this); Account a = new Account("OwnCloud",\r
+         * AccountAuthenticatorService.ACCOUNT_TYPE);\r
+         * accMgr.addAccountExplicitly(a, "asd", null); finish(); } break; case\r
+         * R.id.button2: setResult(Activity.RESULT_CANCELED); finish(); break; }\r
+         */\r
+    }\r
+\r
+    /*\r
+     * private URI prepareURI() { URI uri = null; String url = ""; try { String\r
+     * username = ((EditText)\r
+     * findViewById(R.id.newSession_username)).getText().toString().trim();\r
+     * String password = ((EditText)\r
+     * findViewById(R.id.newSession_password)).getText().toString().trim();\r
+     * String hostname = ((EditText)\r
+     * findViewById(R.id.newSession_URL)).getText().toString().trim(); String\r
+     * scheme; if (hostname.matches("[A-Za-z]://")) { scheme =\r
+     * hostname.substring(0, hostname.indexOf("://")+3); hostname =\r
+     * hostname.substring(hostname.indexOf("://")+3); } else { scheme =\r
+     * "http://"; } if (!username.equals("")) { if (!password.equals("")) {\r
+     * username += ":" + password + "@"; } else { username += "@"; } } url =\r
+     * scheme + username + hostname; Log.i(TAG, url); uri = new URI(url); }\r
+     * catch (URISyntaxException e) { Log.e(TAG, "Incorrect URI syntax " +\r
+     * e.getLocalizedMessage()); Toast.makeText(this,\r
+     * R.string.new_session_uri_error, Toast.LENGTH_LONG).show(); } return uri;\r
+     * }\r
+     * \r
+     * private boolean isNameValid(String string) { return\r
+     * string.matches("[A-Za-z0-9 _-]*"); }\r
+     */\r
+\r
+    @Override\r
+    public void onBackPressed() {\r
+        setResult(Activity.RESULT_CANCELED);\r
+        super.onBackPressed();\r
+    }\r
+\r
+}\r
diff --git a/src/com/owncloud/android/ui/activity/TransferServiceGetter.java b/src/com/owncloud/android/ui/activity/TransferServiceGetter.java
new file mode 100644 (file)
index 0000000..fbc348d
--- /dev/null
@@ -0,0 +1,42 @@
+/* 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.ui.activity;
+
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
+import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
+
+public interface TransferServiceGetter {
+
+    /**
+     * Callback method invoked when the parent activity is fully created to get a reference to the FileDownloader service API.
+     * 
+     * @return  Directory to list firstly. Can be NULL.
+     */
+    public FileDownloaderBinder getFileDownloaderBinder();
+
+    
+    /**
+     * Callback method invoked when the parent activity is fully created to get a reference to the FileUploader service API.
+     * 
+     * @return  Directory to list firstly. Can be NULL.
+     */
+    public FileUploaderBinder getFileUploaderBinder();
+
+
+}
diff --git a/src/com/owncloud/android/ui/activity/UploadFilesActivity.java b/src/com/owncloud/android/ui/activity/UploadFilesActivity.java
new file mode 100644 (file)
index 0000000..ab7bb46
--- /dev/null
@@ -0,0 +1,395 @@
+/* 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 android.accounts.Account;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Environment;
+import android.support.v4.app.DialogFragment;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.ActionBar.OnNavigationListener;
+import com.actionbarsherlock.view.MenuItem;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.ui.CustomButton;
+import com.owncloud.android.ui.dialog.IndeterminateProgressDialog;
+import com.owncloud.android.ui.fragment.ConfirmationDialogFragment;
+import com.owncloud.android.ui.fragment.LocalFileListFragment;
+import com.owncloud.android.ui.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
+import com.owncloud.android.utils.FileStorageUtils;
+
+
+/**
+ * Displays local files and let the user choose what of them wants to upload
+ * to the current ownCloud account
+ * 
+ * @author David A. Velasco
+ * 
+ */
+
+public class UploadFilesActivity extends FileActivity implements
+    LocalFileListFragment.ContainerActivity, OnNavigationListener, OnClickListener, ConfirmationDialogFragmentListener {
+    
+    private ArrayAdapter<String> mDirectories;
+    private File mCurrentDir = null;
+    private LocalFileListFragment mFileListFragment;
+    private CustomButton mCancelBtn;
+    private CustomButton mUploadBtn;
+    private Account mAccountOnCreation;
+    private DialogFragment mCurrentDialog;
+    
+    public static final String EXTRA_CHOSEN_FILES = UploadFilesActivity.class.getCanonicalName() + ".EXTRA_CHOSEN_FILES";
+
+    public static final int RESULT_OK_AND_MOVE = RESULT_FIRST_USER; 
+    
+    private static final String KEY_DIRECTORY_PATH = UploadFilesActivity.class.getCanonicalName() + ".KEY_DIRECTORY_PATH";
+    private static final String TAG = "UploadFilesActivity";
+    private static final String WAIT_DIALOG_TAG = "WAIT";
+    private static final String QUERY_TO_MOVE_DIALOG_TAG = "QUERY_TO_MOVE";
+    
+    
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        Log_OC.d(TAG, "onCreate() start");
+        super.onCreate(savedInstanceState);
+
+        if(savedInstanceState != null) {
+            mCurrentDir = new File(savedInstanceState.getString(UploadFilesActivity.KEY_DIRECTORY_PATH));
+        } else {
+            mCurrentDir = Environment.getExternalStorageDirectory();
+        }
+        
+        mAccountOnCreation = getAccount();
+                
+        /// USER INTERFACE
+            
+        // Drop-down navigation 
+        mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
+        File currDir = mCurrentDir;
+        while(currDir != null && currDir.getParentFile() != null) {
+            mDirectories.add(currDir.getName());
+            currDir = currDir.getParentFile();
+        }
+        mDirectories.add(File.separator);
+
+        // Inflate and set the layout view
+        setContentView(R.layout.upload_files_layout);
+        mFileListFragment = (LocalFileListFragment) getSupportFragmentManager().findFragmentById(R.id.local_files_list);
+        
+        
+        // Set input controllers
+        mCancelBtn = (CustomButton) findViewById(R.id.upload_files_btn_cancel);
+        mCancelBtn.setOnClickListener(this);
+        mUploadBtn = (CustomButton) findViewById(R.id.upload_files_btn_upload);
+        mUploadBtn.setOnClickListener(this);
+        
+            
+        // Action bar setup
+        ActionBar actionBar = getSupportActionBar();
+        actionBar.setHomeButtonEnabled(true);   // mandatory since Android ICS, according to the official documentation
+        actionBar.setDisplayHomeAsUpEnabled(mCurrentDir != null && mCurrentDir.getName() != null);
+        actionBar.setDisplayShowTitleEnabled(false);
+        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
+        actionBar.setListNavigationCallbacks(mDirectories, this);
+        
+        // wait dialog
+        if (mCurrentDialog != null) {
+            mCurrentDialog.dismiss();
+            mCurrentDialog = null;
+        }
+            
+        Log_OC.d(TAG, "onCreate() end");
+    }
+
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        boolean retval = true;
+        switch (item.getItemId()) {
+            case android.R.id.home: {
+                if(mCurrentDir != null && mCurrentDir.getParentFile() != null){
+                    onBackPressed(); 
+                }
+                break;
+            }
+            default:
+                retval = super.onOptionsItemSelected(item);
+        }
+        return retval;
+    }
+
+    
+    @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;
+    }
+
+    
+    @Override
+    public void onBackPressed() {
+        if (mDirectories.getCount() <= 1) {
+            finish();
+            return;
+        }
+        popDirname();
+        mFileListFragment.onNavigateUp();
+        mCurrentDir = mFileListFragment.getCurrentDirectory();
+        
+        if(mCurrentDir.getParentFile() == null){
+            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(TAG, "onSaveInstanceState() start");
+        super.onSaveInstanceState(outState);
+        outState.putString(UploadFilesActivity.KEY_DIRECTORY_PATH, mCurrentDir.getAbsolutePath());
+        Log_OC.d(TAG, "onSaveInstanceState() end");
+    }
+
+    
+    /**
+     * Pushes a directory to the drop down list
+     * @param directory to push
+     * @throws IllegalArgumentException If the {@link File#isDirectory()} returns false.
+     */
+    public void pushDirname(File directory) {
+        if(!directory.isDirectory()){
+            throw new IllegalArgumentException("Only directories may be pushed!");
+        }
+        mDirectories.insert(directory.getName(), 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(UploadFilesActivity 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;
+        }
+    
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onDirectoryClick(File directory) {
+        pushDirname(directory);
+        ActionBar actionBar = getSupportActionBar();
+        actionBar.setDisplayHomeAsUpEnabled(true);
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onFileClick(File file) {
+        // nothing to do
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public File getInitialDirectory() {
+        return mCurrentDir;
+    }
+
+
+    /**
+     * Performs corresponding action when user presses 'Cancel' or 'Upload' button
+     * 
+     * TODO Make here the real request to the Upload service ; will require to receive the account and 
+     * target folder where the upload must be done in the received intent.
+     */
+    @Override
+    public void onClick(View v) {
+        if (v.getId() == R.id.upload_files_btn_cancel) {
+            setResult(RESULT_CANCELED);
+            finish();
+            
+        } else if (v.getId() == R.id.upload_files_btn_upload) {
+            new CheckAvailableSpaceTask().execute();
+        }
+    }
+
+
+    /**
+     * Asynchronous task checking if there is space enough to copy all the files chosen
+     * to upload into the ownCloud local folder.
+     * 
+     * Maybe an AsyncTask is not strictly necessary, but who really knows.
+     * 
+     * @author David A. Velasco
+     */
+    private class CheckAvailableSpaceTask extends AsyncTask<Void, Void, Boolean> {
+
+        /**
+         * Updates the UI before trying the movement
+         */
+        @Override
+        protected void onPreExecute () {
+            /// progress dialog and disable 'Move' button
+            mCurrentDialog = IndeterminateProgressDialog.newInstance(R.string.wait_a_moment, false);
+            mCurrentDialog.show(getSupportFragmentManager(), WAIT_DIALOG_TAG);
+        }
+        
+        
+        /**
+         * Checks the available space
+         * 
+         * @return     'True' if there is space enough.
+         */
+        @Override
+        protected Boolean doInBackground(Void... params) {
+            String[] checkedFilePaths = mFileListFragment.getCheckedFilePaths();
+            long total = 0;
+            for (int i=0; checkedFilePaths != null && i < checkedFilePaths.length ; i++) {
+                String localPath = checkedFilePaths[i];
+                File localFile = new File(localPath);
+                total += localFile.length();
+            }
+            return (FileStorageUtils.getUsableSpace(mAccountOnCreation.name) >= total);
+        }
+
+        /**
+         * Updates the activity UI after the check of space is done.
+         * 
+         * If there is not space enough. shows a new dialog to query the user if wants to move the files instead
+         * of copy them.
+         * 
+         * @param result        'True' when there is space enough to copy all the selected files.
+         */
+        @Override
+        protected void onPostExecute(Boolean result) {
+            mCurrentDialog.dismiss();
+            mCurrentDialog = null;
+            
+            if (result) {
+                // return the list of selected files (success)
+                Intent data = new Intent();
+                data.putExtra(EXTRA_CHOSEN_FILES, mFileListFragment.getCheckedFilePaths());
+                setResult(RESULT_OK, data);
+                finish();
+                
+            } else {
+                // show a dialog to query the user if wants to move the selected files to the ownCloud folder instead of copying
+                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);
+                dialog.show(getSupportFragmentManager(), QUERY_TO_MOVE_DIALOG_TAG);
+            }
+        }
+    }
+
+    @Override
+    public void onConfirmation(String 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();
+            data.putExtra(EXTRA_CHOSEN_FILES, mFileListFragment.getCheckedFilePaths());
+            setResult(RESULT_OK_AND_MOVE, data);
+            finish();
+        }
+    }
+
+
+    @Override
+    public void onNeutral(String callerTag) {
+        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_OC.d(TAG, "Negative button in dialog was clicked; dialog tag is " + callerTag);
+    }
+
+
+    @Override
+    protected void onAccountSet(boolean stateWasRecovered) {
+        if (getAccount() != null) {
+            if (!mAccountOnCreation.equals(getAccount())) {
+                setResult(RESULT_CANCELED);
+                finish();
+            }
+            
+        } else {
+            Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
+            setResult(RESULT_CANCELED);
+            finish();
+        }
+    }    
+
+    
+}
diff --git a/src/com/owncloud/android/ui/adapter/FileListListAdapter.java b/src/com/owncloud/android/ui/adapter/FileListListAdapter.java
new file mode 100644 (file)
index 0000000..a2651fa
--- /dev/null
@@ -0,0 +1,210 @@
+/* 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 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
+package com.owncloud.android.ui.adapter;\r
+\r
+import android.accounts.Account;\r
+import android.content.Context;\r
+import android.view.LayoutInflater;\r
+import android.view.View;\r
+import android.view.ViewGroup;\r
+import android.widget.BaseAdapter;\r
+import android.widget.ImageView;\r
+import android.widget.ListAdapter;\r
+import android.widget.ListView;\r
+import android.widget.TextView;\r
+\r
+\r
+import java.util.Vector;\r
+\r
+import com.owncloud.android.DisplayUtils;\r
+import com.owncloud.android.R;\r
+import com.owncloud.android.authentication.AccountUtils;\r
+import com.owncloud.android.datamodel.DataStorageManager;\r
+import com.owncloud.android.datamodel.OCFile;\r
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;\r
+import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;\r
+import com.owncloud.android.ui.activity.TransferServiceGetter;\r
+\r
+\r
+/**\r
+ * This Adapter populates a ListView with all files and folders in an ownCloud\r
+ * instance.\r
+ * \r
+ * @author Bartek Przybylski\r
+ * \r
+ */\r
+public class FileListListAdapter extends BaseAdapter implements ListAdapter {\r
+    private Context mContext;\r
+    private OCFile mFile = null;\r
+    private Vector<OCFile> mFiles = null;\r
+    private DataStorageManager mStorageManager;\r
+    private Account mAccount;\r
+    private TransferServiceGetter mTransferServiceGetter;\r
+    \r
+    public FileListListAdapter(Context context, TransferServiceGetter transferServiceGetter) {\r
+        mContext = context;\r
+        mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);\r
+        mTransferServiceGetter = transferServiceGetter;\r
+    }\r
+\r
+    @Override\r
+    public boolean areAllItemsEnabled() {\r
+        return true;\r
+    }\r
+\r
+    @Override\r
+    public boolean isEnabled(int position) {\r
+        return true;\r
+    }\r
+\r
+    @Override\r
+    public int getCount() {\r
+        return mFiles != null ? mFiles.size() : 0;\r
+    }\r
+\r
+    @Override\r
+    public Object getItem(int position) {\r
+        if (mFiles == null || mFiles.size() <= position)\r
+            return null;\r
+        return mFiles.get(position);\r
+    }\r
+\r
+    @Override\r
+    public long getItemId(int position) {\r
+        if (mFiles == null || mFiles.size() <= position)\r
+            return 0;\r
+        return mFiles.get(position).getFileId();\r
+    }\r
+\r
+    @Override\r
+    public int getItemViewType(int position) {\r
+        return 0;\r
+    }\r
+\r
+    @Override\r
+    public View getView(int position, View convertView, ViewGroup parent) {\r
+        View view = convertView;\r
+        if (view == null) {\r
+            LayoutInflater inflator = (LayoutInflater) mContext\r
+                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);\r
+            view = inflator.inflate(R.layout.list_item, null);\r
+        }\r
+    \r
+        if (mFiles != null && mFiles.size() > position) {\r
+            OCFile file = mFiles.get(position);\r
+            TextView fileName = (TextView) view.findViewById(R.id.Filename);\r
+            String name = file.getFileName();\r
+\r
+            fileName.setText(name);\r
+            ImageView fileIcon = (ImageView) view.findViewById(R.id.imageView1);\r
+            fileIcon.setImageResource(DisplayUtils.getResourceId(file.getMimetype()));\r
+            ImageView localStateView = (ImageView) view.findViewById(R.id.imageView2);\r
+            FileDownloaderBinder downloaderBinder = mTransferServiceGetter.getFileDownloaderBinder();\r
+            FileUploaderBinder uploaderBinder = mTransferServiceGetter.getFileUploaderBinder();\r
+            if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) {\r
+                localStateView.setImageResource(R.drawable.downloading_file_indicator);\r
+                localStateView.setVisibility(View.VISIBLE);\r
+            } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file)) {\r
+                localStateView.setImageResource(R.drawable.uploading_file_indicator);\r
+                localStateView.setVisibility(View.VISIBLE);\r
+            } else if (file.isDown()) {\r
+                localStateView.setImageResource(R.drawable.local_file_indicator);\r
+                localStateView.setVisibility(View.VISIBLE);\r
+            } else {\r
+                localStateView.setVisibility(View.INVISIBLE);\r
+            }\r
+            \r
+            TextView fileSizeV = (TextView) view.findViewById(R.id.file_size);\r
+            TextView lastModV = (TextView) view.findViewById(R.id.last_mod);\r
+            ImageView checkBoxV = (ImageView) view.findViewById(R.id.custom_checkbox);\r
+            \r
+            if (!file.isDirectory()) {\r
+                fileSizeV.setVisibility(View.VISIBLE);\r
+                fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));\r
+                lastModV.setVisibility(View.VISIBLE);\r
+                lastModV.setText(DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp()));\r
+                // this if-else is needed even thoe fav icon is visible by default\r
+                // because android reuses views in listview\r
+                if (!file.keepInSync()) {\r
+                    view.findViewById(R.id.imageView3).setVisibility(View.GONE);\r
+                } else {\r
+                    view.findViewById(R.id.imageView3).setVisibility(View.VISIBLE);\r
+                }\r
+                \r
+                ListView parentList = (ListView)parent;\r
+                if (parentList.getChoiceMode() == ListView.CHOICE_MODE_NONE) { \r
+                    checkBoxV.setVisibility(View.GONE);\r
+                } else {\r
+                    if (parentList.isItemChecked(position)) {\r
+                        checkBoxV.setImageResource(android.R.drawable.checkbox_on_background);\r
+                    } else {\r
+                        checkBoxV.setImageResource(android.R.drawable.checkbox_off_background);\r
+                    }\r
+                    checkBoxV.setVisibility(View.VISIBLE);\r
+                }\r
+                \r
+            } \r
+            else {\r
+                \r
+                fileSizeV.setVisibility(View.VISIBLE);\r
+                fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));\r
+                lastModV.setVisibility(View.VISIBLE);\r
+                lastModV.setText(DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp()));\r
+               checkBoxV.setVisibility(View.GONE);\r
+               view.findViewById(R.id.imageView3).setVisibility(View.GONE);\r
+            }\r
+        }\r
+\r
+        return view;\r
+    }\r
+\r
+    @Override\r
+    public int getViewTypeCount() {\r
+        return 1;\r
+    }\r
+\r
+    @Override\r
+    public boolean hasStableIds() {\r
+        return true;\r
+    }\r
+\r
+    @Override\r
+    public boolean isEmpty() {\r
+        return (mFiles == null || mFiles.isEmpty());\r
+    }\r
+\r
+    /**\r
+     * Change the adapted directory for a new one\r
+     * @param directory                 New file to adapt. Can be NULL, meaning "no content to adapt".\r
+     * @param updatedStorageManager     Optional updated storage manager; used to replace mStorageManager if is different (and not NULL)\r
+     */\r
+    public void swapDirectory(OCFile directory, DataStorageManager updatedStorageManager) {\r
+        mFile = directory;\r
+        if (updatedStorageManager != null && updatedStorageManager != mStorageManager) {\r
+            mStorageManager = updatedStorageManager;\r
+            mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);\r
+        }\r
+        if (mStorageManager != null) {\r
+            mFiles = mStorageManager.getDirectoryContent(mFile);\r
+        } else {\r
+            mFiles = null;\r
+        }\r
+        notifyDataSetChanged();\r
+    }\r
+    \r
+}\r
diff --git a/src/com/owncloud/android/ui/adapter/LandingScreenAdapter.java b/src/com/owncloud/android/ui/adapter/LandingScreenAdapter.java
new file mode 100644 (file)
index 0000000..cea4894
--- /dev/null
@@ -0,0 +1,113 @@
+/* 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 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
+package com.owncloud.android.ui.adapter;\r
+\r
+\r
+import com.owncloud.android.R;\r
+import com.owncloud.android.authentication.AccountUtils;\r
+import com.owncloud.android.ui.activity.FileDisplayActivity;\r
+import com.owncloud.android.ui.activity.Preferences;\r
+\r
+import android.content.Context;\r
+import android.content.Intent;\r
+import android.view.LayoutInflater;\r
+import android.view.View;\r
+import android.view.ViewGroup;\r
+import android.widget.BaseAdapter;\r
+import android.widget.ImageView;\r
+import android.widget.TextView;\r
+\r
+/**\r
+ * Populates the landing screen icons.\r
+ * \r
+ * @author Lennart Rosam\r
+ * \r
+ */\r
+public class LandingScreenAdapter extends BaseAdapter {\r
+\r
+    private Context mContext;\r
+\r
+    private final Integer[] mLandingScreenIcons = { R.drawable.home,\r
+            R.drawable.music, R.drawable.contacts, R.drawable.calendar,\r
+            android.R.drawable.ic_menu_agenda, R.drawable.settings };\r
+\r
+    private final Integer[] mLandingScreenTexts = { R.string.main_files,\r
+            R.string.main_music, R.string.main_contacts,\r
+            R.string.main_calendar, R.string.main_bookmarks,\r
+            R.string.main_settings };\r
+\r
+    public LandingScreenAdapter(Context context) {\r
+        mContext = context;\r
+    }\r
+\r
+    @Override\r
+    public int getCount() {\r
+        return mLandingScreenIcons.length;\r
+    }\r
+\r
+    @Override\r
+    /**\r
+     * Returns the Intent associated with this object\r
+     * or null if the functionality is not yet implemented\r
+     */\r
+    public Object getItem(int position) {\r
+        Intent intent = new Intent();\r
+\r
+        switch (position) {\r
+        case 0:\r
+            /*\r
+             * The FileDisplayActivity requires the ownCloud account as an\r
+             * parcableExtra. We will put in the one that is selected in the\r
+             * preferences\r
+             */\r
+            intent.setClass(mContext, FileDisplayActivity.class);\r
+            intent.putExtra("ACCOUNT",\r
+                    AccountUtils.getCurrentOwnCloudAccount(mContext));\r
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\r
+            break;\r
+        case 5:\r
+            intent.setClass(mContext, Preferences.class);\r
+            break;\r
+        default:\r
+            intent = null;\r
+        }\r
+        return intent;\r
+    }\r
+\r
+    @Override\r
+    public long getItemId(int position) {\r
+        return position;\r
+    }\r
+\r
+    @Override\r
+    public View getView(int position, View convertView, ViewGroup parent) {\r
+        if (convertView == null) {\r
+            LayoutInflater inflator = LayoutInflater.from(mContext);\r
+            convertView = inflator.inflate(R.layout.landing_page_item, null);\r
+\r
+            ImageView icon = (ImageView) convertView\r
+                    .findViewById(R.id.gridImage);\r
+            TextView iconText = (TextView) convertView\r
+                    .findViewById(R.id.gridText);\r
+\r
+            icon.setImageResource(mLandingScreenIcons[position]);\r
+            iconText.setText(mLandingScreenTexts[position]);\r
+        }\r
+        return convertView;\r
+    }\r
+}\r
diff --git a/src/com/owncloud/android/ui/adapter/LocalFileListAdapter.java b/src/com/owncloud/android/ui/adapter/LocalFileListAdapter.java
new file mode 100644 (file)
index 0000000..ff883c5
--- /dev/null
@@ -0,0 +1,185 @@
+/* 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.adapter;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Comparator;
+
+import com.owncloud.android.DisplayUtils;
+import com.owncloud.android.R;
+
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * This Adapter populates a ListView with all files and directories contained
+ * in a local directory
+ * 
+ * @author David A. Velasco
+ * 
+ */
+public class LocalFileListAdapter extends BaseAdapter implements ListAdapter {
+    
+    private Context mContext;
+    private File mDirectory;
+    private File[] mFiles = null;
+
+    public LocalFileListAdapter(File directory, Context context) {
+        mContext = context;
+        swapDirectory(directory);
+    }
+
+    @Override
+    public boolean areAllItemsEnabled() {
+        return true;
+    }
+
+    @Override
+    public boolean isEnabled(int position) {
+        return true;
+    }
+
+    @Override
+    public int getCount() {
+        return mFiles != null ? mFiles.length : 0;
+    }
+
+    @Override
+    public Object getItem(int position) {
+        if (mFiles == null || mFiles.length <= position)
+            return null;
+        return mFiles[position];
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return mFiles != null && mFiles.length <= position ? position : -1;
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        return 0;
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        View view = convertView;
+        if (view == null) {
+            LayoutInflater inflator = (LayoutInflater) mContext
+                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+            view = inflator.inflate(R.layout.list_item, null);
+        }
+        if (mFiles != null && mFiles.length > position) {
+            File file = mFiles[position];
+            
+            TextView fileName = (TextView) view.findViewById(R.id.Filename);
+            String name = file.getName();
+            fileName.setText(name);
+            
+            ImageView fileIcon = (ImageView) view.findViewById(R.id.imageView1);
+            if (!file.isDirectory()) {
+                fileIcon.setImageResource(R.drawable.file);
+            } else {
+                fileIcon.setImageResource(R.drawable.ic_menu_archive);
+            }
+
+            TextView fileSizeV = (TextView) view.findViewById(R.id.file_size);
+            TextView lastModV = (TextView) view.findViewById(R.id.last_mod);
+            ImageView checkBoxV = (ImageView) view.findViewById(R.id.custom_checkbox);
+            if (!file.isDirectory()) {
+                fileSizeV.setVisibility(View.VISIBLE);
+                fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.length()));
+                lastModV.setVisibility(View.VISIBLE);
+                lastModV.setText(DisplayUtils.unixTimeToHumanReadable(file.lastModified()));
+                ListView parentList = (ListView)parent;
+                if (parentList.getChoiceMode() == ListView.CHOICE_MODE_NONE) { 
+                    checkBoxV.setVisibility(View.GONE);
+                } else {
+                    if (parentList.isItemChecked(position)) {
+                        checkBoxV.setImageResource(android.R.drawable.checkbox_on_background);
+                    } else {
+                        checkBoxV.setImageResource(android.R.drawable.checkbox_off_background);
+                    }
+                    checkBoxV.setVisibility(View.VISIBLE);
+                }
+
+            } else {
+                fileSizeV.setVisibility(View.GONE);
+                lastModV.setVisibility(View.GONE);
+                checkBoxV.setVisibility(View.GONE);
+            }
+            
+            view.findViewById(R.id.imageView2).setVisibility(View.INVISIBLE);   // not GONE; the alignment changes; ugly way to keep it
+            view.findViewById(R.id.imageView3).setVisibility(View.GONE);
+        }
+
+        return view;
+    }
+
+    @Override
+    public int getViewTypeCount() {
+        return 1;
+    }
+
+    @Override
+    public boolean hasStableIds() {
+        return false;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return (mFiles == null || mFiles.length == 0);
+    }
+
+    /**
+     * Change the adapted directory for a new one
+     * @param directory     New file to adapt. Can be NULL, meaning "no content to adapt".
+     */
+    public void swapDirectory(File directory) {
+        mDirectory = directory;
+        mFiles = (mDirectory != null ? mDirectory.listFiles() : null);
+        if (mFiles != null) {
+            Arrays.sort(mFiles, new Comparator<File>() {
+                @Override
+                public int compare(File lhs, File rhs) {
+                    if (lhs.isDirectory() && !rhs.isDirectory()) {
+                        return -1;
+                    } else if (!lhs.isDirectory() && rhs.isDirectory()) {
+                        return 1;
+                    }
+                    return compareNames(lhs, rhs);
+                }
+            
+                private int compareNames(File lhs, File rhs) {
+                    return lhs.getName().toLowerCase().compareTo(rhs.getName().toLowerCase());                
+                }
+            
+            });
+        }
+        notifyDataSetChanged();
+    }
+}
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..ae4335e
--- /dev/null
@@ -0,0 +1,55 @@
+package com.owncloud.android.ui.adapter;
+
+import java.io.File;
+
+import com.owncloud.android.R;
+
+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;
+
+
+
+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;
+    }
+}
diff --git a/src/com/owncloud/android/ui/dialog/ChangelogDialog.java b/src/com/owncloud/android/ui/dialog/ChangelogDialog.java
new file mode 100644 (file)
index 0000000..676bea5
--- /dev/null
@@ -0,0 +1,104 @@
+/* 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.dialog;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.webkit.WebView;
+
+import com.actionbarsherlock.app.SherlockDialogFragment;
+import com.owncloud.android.R;
+
+
+/**
+ * Dialog to show the contents of res/raw/CHANGELOG.txt
+ */
+public class ChangelogDialog extends SherlockDialogFragment {
+
+    private static final String ARG_CANCELABLE = ChangelogDialog.class.getCanonicalName() + ".ARG_CANCELABLE";
+
+
+    /**
+     * Public factory method to get dialog instances.
+     * 
+     * @param cancelable    If 'true', the dialog can be cancelled by the user input (BACK button, touch outside...)
+     * @return              New dialog instance, ready to show.
+     */
+    public static ChangelogDialog newInstance(boolean cancelable) {
+        ChangelogDialog fragment = new ChangelogDialog();
+        Bundle args = new Bundle();
+        args.putBoolean(ARG_CANCELABLE, cancelable);
+        fragment.setArguments(args);
+        return fragment;
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        /// load the custom view to insert in the dialog, between title and 
+        WebView webview = new WebView(getActivity());
+        webview.loadUrl("file:///android_res/raw/" + getResources().getResourceEntryName(R.raw.changelog) + ".html");
+        
+        /// build the dialog
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        
+        Dialog dialog = builder.setView(webview)
+                                .setIcon(R.drawable.icon)
+                                //.setTitle(R.string.whats_new)
+                                .setPositiveButton(R.string.common_ok,
+                                        new DialogInterface.OnClickListener() {
+                                    @Override
+                                    public void onClick(DialogInterface dialog, int which) {
+                                        dialog.dismiss();
+                                    }
+                                }).create();
+        
+        dialog.setCancelable(getArguments().getBoolean(ARG_CANCELABLE));
+        return dialog;
+    } 
+    
+    /**
+     * {@inheritDoc}
+     *-/
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        /// load the custom layout
+        View view = inflater.inflate(R.layout.fragment_changelog, container);
+        mEditText = (EditText) view.findViewById(R.id.txt_your_name);
+        getDialog().setTitle(R.string.whats_new);
+        
+        /// read full contents of the change log file (don't make it too big)
+        InputStream changeLogStream = getResources().openRawResource(R.raw.changelog);
+        Scanner scanner = new java.util.Scanner(changeLogStream).useDelimiter("\\A");
+        String text = scanner.hasNext() ? scanner.next() : "";
+        
+        /// make clickable the links in the change log file
+        SpannableString sText = new SpannableString(text);
+        Linkify.addLinks(sText, Linkify.ALL);
+
+        return view;
+    }
+    */
+}
+
+
diff --git a/src/com/owncloud/android/ui/dialog/ConflictsResolveDialog.java b/src/com/owncloud/android/ui/dialog/ConflictsResolveDialog.java
new file mode 100644 (file)
index 0000000..1998fcb
--- /dev/null
@@ -0,0 +1,121 @@
+/* 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.ui.dialog;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+
+import com.actionbarsherlock.app.SherlockDialogFragment;
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.owncloud.android.R;
+
+
+/**
+ * Dialog which will be displayed to user upon keep-in-sync file conflict.
+ * 
+ * @author Bartek Przybylski
+ *
+ */
+public class ConflictsResolveDialog extends SherlockDialogFragment {
+
+    public static enum Decision { 
+        CANCEL,
+        KEEP_BOTH,
+        OVERWRITE
+    }
+    
+    OnConflictDecisionMadeListener mListener;
+    
+    public static ConflictsResolveDialog newInstance(String path, OnConflictDecisionMadeListener listener) {
+        ConflictsResolveDialog f = new ConflictsResolveDialog();
+        Bundle args = new Bundle();
+        args.putString("remotepath", path);
+        f.setArguments(args);
+        f.mListener = listener;
+        return f;
+    }
+    
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        String remotepath = getArguments().getString("remotepath");
+        return new AlertDialog.Builder(getSherlockActivity())
+                   .setIcon(R.drawable.icon)
+                   .setTitle(R.string.conflict_title)
+                   .setMessage(String.format(getString(R.string.conflict_message), remotepath))
+                   .setPositiveButton(R.string.conflict_overwrite,
+                       new DialogInterface.OnClickListener() {
+
+                           @Override
+                           public void onClick(DialogInterface dialog, int which) {
+                               if (mListener != null)
+                                   mListener.ConflictDecisionMade(Decision.OVERWRITE);
+                           }
+                       })
+                   .setNeutralButton(R.string.conflict_keep_both,
+                       new DialogInterface.OnClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which) {
+                                if (mListener != null)
+                                    mListener.ConflictDecisionMade(Decision.KEEP_BOTH);
+                            }
+                        })
+                   .setNegativeButton(R.string.conflict_dont_upload,
+                       new DialogInterface.OnClickListener() {
+                           @Override
+                           public void onClick(DialogInterface dialog, int which) {
+                               if (mListener != null)
+                                   mListener.ConflictDecisionMade(Decision.CANCEL);
+                           }
+                   })
+                   .create();
+    }
+    
+    public void showDialog(SherlockFragmentActivity activity) {
+        Fragment prev = activity.getSupportFragmentManager().findFragmentByTag("dialog");
+        FragmentTransaction ft = activity.getSupportFragmentManager().beginTransaction();
+        if (prev != null) {
+            ft.remove(prev);
+        }
+        ft.addToBackStack(null);
+
+        this.show(ft, "dialog");
+    }
+
+    public void dismissDialog(SherlockFragmentActivity activity) {
+        Fragment prev = activity.getSupportFragmentManager().findFragmentByTag(getTag());
+        if (prev != null) {
+            FragmentTransaction ft = activity.getSupportFragmentManager().beginTransaction();
+            ft.remove(prev);
+            ft.commit();
+        }
+    }
+    
+    @Override
+    public void onCancel(DialogInterface dialog) {
+        mListener.ConflictDecisionMade(Decision.CANCEL);
+    }
+    
+    public interface OnConflictDecisionMadeListener {
+        public void ConflictDecisionMade(Decision decision);
+    }
+}
diff --git a/src/com/owncloud/android/ui/dialog/EditNameDialog.java b/src/com/owncloud/android/ui/dialog/EditNameDialog.java
new file mode 100644 (file)
index 0000000..4d6243a
--- /dev/null
@@ -0,0 +1,173 @@
+/* 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.dialog;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+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;
+import com.owncloud.android.R;
+
+
+
+/**
+ * Dialog to request the user to input a name, optionally initialized with a former name.
+ * 
+ * @author Bartek Przybylski
+ * @author David A. Velasco
+ */
+public class EditNameDialog extends SherlockDialogFragment implements DialogInterface.OnClickListener {
+
+    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_SELECTION_START = "SELECTION_START";
+    protected static final String ARG_SELECTION_END = "SELECTION_END";
+    
+    private String mNewFilename;
+    private boolean mResult;
+    private EditNameDialogListener mListener;
+    
+    /**
+     * 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 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, 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;
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        String currentName = getArguments().getString(ARG_NAME);
+        if (currentName == null)
+            currentName = "";
+        String title = getArguments().getString(ARG_TITLE);
+        
+        // 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
+        EditText inputText = ((EditText)v.findViewById(R.id.user_input));
+        inputText.setText(currentName);
+        
+        // Set it to the dialog 
+        AlertDialog.Builder builder = new AlertDialog.Builder(getSherlockActivity());
+        builder.setView(v)
+               .setPositiveButton(R.string.common_ok, this)
+               .setNegativeButton(R.string.common_cancel, this);
+
+        if (title != null) {
+            builder.setTitle(title);
+        }
+        
+        mResult = false;
+        
+        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;
+    }    
+
+    
+    /**
+     * Performs the corresponding action when a dialog button is clicked.
+     * 
+     * Saves the text in the input field to be accessed through {@link #getNewFilename()} when the positive
+     * button is clicked.
+     * 
+     * Notify the current listener in any case.
+     */
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        switch (which) {
+            case AlertDialog.BUTTON_POSITIVE: {
+                mNewFilename = ((TextView)(getDialog().findViewById(R.id.user_input))).getText().toString();
+                mResult = true;
+            }
+            case AlertDialog.BUTTON_NEGATIVE: { // fall through
+                dismiss();
+                if (mListener != null)
+                    mListener.onDismiss(this);
+            }
+        }
+    }
+    
+    protected void setOnDismissListener(EditNameDialogListener listener) {
+        mListener = listener;
+    }
+    
+    /**
+     * Returns the text in the input field after the user clicked the positive button.
+     * 
+     * @return      Text in the input field.
+     */
+    public String getNewFilename() {
+        return mNewFilename;
+    }
+    
+    /**
+     * 
+     * @return      True when the user clicked the positive button.
+     */
+    public boolean getResult() {
+        return mResult;
+    }
+
+    
+    /**
+     * Interface to receive a notification when any button in the dialog is clicked.
+     */
+    public interface EditNameDialogListener {
+        public void onDismiss(EditNameDialog dialog);
+    }
+
+
+}
+
diff --git a/src/com/owncloud/android/ui/dialog/IndeterminateProgressDialog.java b/src/com/owncloud/android/ui/dialog/IndeterminateProgressDialog.java
new file mode 100644 (file)
index 0000000..dbd3d99
--- /dev/null
@@ -0,0 +1,92 @@
+/* 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.dialog;
+
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnKeyListener;
+import android.os.Bundle;
+import android.view.KeyEvent;
+
+import com.actionbarsherlock.app.SherlockDialogFragment;
+import com.owncloud.android.R;
+
+
+public class IndeterminateProgressDialog extends SherlockDialogFragment {
+
+    private static final String ARG_MESSAGE_ID = IndeterminateProgressDialog.class.getCanonicalName() + ".ARG_MESSAGE_ID";
+    private static final String ARG_CANCELABLE = IndeterminateProgressDialog.class.getCanonicalName() + ".ARG_CANCELABLE";
+
+
+    /**
+     * Public factory method to get dialog instances.
+     * 
+     * @param messageId     Resource id for a message to show in the dialog.
+     * @param cancelable    If 'true', the dialog can be cancelled by the user input (BACK button, touch outside...)
+     * @return              New dialog instance, ready to show.
+     */
+    public static IndeterminateProgressDialog newInstance(int messageId, boolean cancelable) {
+        IndeterminateProgressDialog fragment = new IndeterminateProgressDialog();
+        Bundle args = new Bundle();
+        args.putInt(ARG_MESSAGE_ID, messageId);
+        args.putBoolean(ARG_CANCELABLE, cancelable);
+        fragment.setArguments(args);
+        return fragment;
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        /// create indeterminate progress dialog
+        final ProgressDialog dialog = new ProgressDialog(getActivity());
+        dialog.setIndeterminate(true);
+        
+        /// set message
+        int messageId = getArguments().getInt(ARG_MESSAGE_ID, R.string.placeholder_sentence);
+        dialog.setMessage(getString(messageId));
+        
+        /// set cancellation behavior
+        boolean cancelable = getArguments().getBoolean(ARG_CANCELABLE, false);
+        if (!cancelable) {
+            dialog.setCancelable(false);
+            // disable the back button
+            OnKeyListener keyListener = new OnKeyListener() {
+                @Override
+                public boolean onKey(DialogInterface dialog, int keyCode,
+                        KeyEvent event) {
+
+                    if( keyCode == KeyEvent.KEYCODE_BACK){                  
+                        return true;
+                    }
+                    return false;
+                }
+
+            };
+            dialog.setOnKeyListener(keyListener);
+        }
+        
+        return dialog;
+    }    
+    
+}
+
+
diff --git a/src/com/owncloud/android/ui/dialog/LoadingDialog.java b/src/com/owncloud/android/ui/dialog/LoadingDialog.java
new file mode 100644 (file)
index 0000000..2203e03
--- /dev/null
@@ -0,0 +1,58 @@
+package com.owncloud.android.ui.dialog;
+
+import com.owncloud.android.R;
+
+import android.app.Dialog;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.TextView;
+
+public class LoadingDialog extends DialogFragment {
+
+    private String mMessage;
+    
+    public LoadingDialog() {
+        super();
+    }
+    
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setRetainInstance(true);
+        setCancelable(false);
+    }
+
+    public LoadingDialog(String message) {
+        this.mMessage = message;
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        // Create a view by inflating desired layout
+        View v = inflater.inflate(R.layout.loading_dialog, container,  false);
+        
+        // set value
+        TextView tv  = (TextView) v.findViewById(R.id.loadingText);
+        tv.setText(mMessage);
+        
+        return v;
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        Dialog dialog = super.onCreateDialog(savedInstanceState);
+        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+        return dialog;
+    }
+
+    @Override
+    public void onDestroyView() {
+        if (getDialog() != null && getRetainInstance())
+            getDialog().setDismissMessage(null);
+            super.onDestroyView();
+    }
+}
diff --git a/src/com/owncloud/android/ui/dialog/SamlWebViewDialog.java b/src/com/owncloud/android/ui/dialog/SamlWebViewDialog.java
new file mode 100644 (file)
index 0000000..516fce1
--- /dev/null
@@ -0,0 +1,285 @@
+/* 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.dialog;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.app.FragmentManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.CookieManager;
+import android.webkit.CookieSyncManager;
+import android.webkit.WebBackForwardList;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+
+import com.actionbarsherlock.app.SherlockDialogFragment;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.SsoWebViewClient;
+import com.owncloud.android.authentication.SsoWebViewClient.SsoWebViewClientListener;
+
+
+import eu.alefzero.webdav.WebdavClient;
+
+/**
+ * Dialog to show the WebView for SAML Authentication
+ * 
+ * @author Maria Asensio
+ * @author David A. Velasco
+ */
+public class SamlWebViewDialog extends SherlockDialogFragment {
+
+    public final String SAML_DIALOG_TAG = "SamlWebViewDialog";
+    
+    private final static String TAG =  SamlWebViewDialog.class.getSimpleName();
+
+    private static final String ARG_INITIAL_URL = "INITIAL_URL";
+    private static final String ARG_TARGET_URL = "TARGET_URL";
+    private static final String KEY_WEBVIEW_STATE = "WEBVIEW_STATE";
+    
+    private WebView mSsoWebView;
+    private SsoWebViewClient mWebViewClient;
+    
+    private String mInitialUrl;
+    private String mTargetUrl;
+    
+    private Handler mHandler;
+
+    private SsoWebViewClientListener mSsoWebViewClientListener;
+
+    //private View mSsoRootView;
+
+
+    /**
+     * Public factory method to get dialog instances.
+     * 
+     * @param handler
+     * @param Url           Url to open at WebView
+     * @param targetURL     mHostBaseUrl + AccountUtils.getWebdavPath(mDiscoveredVersion, mCurrentAuthTokenType)
+     * @return              New dialog instance, ready to show.
+     */
+    public static SamlWebViewDialog newInstance(String url, String targetUrl) {
+        Log_OC.d(TAG, "New instance");
+        SamlWebViewDialog fragment = new SamlWebViewDialog();
+        Bundle args = new Bundle();
+        args.putString(ARG_INITIAL_URL, url);
+        args.putString(ARG_TARGET_URL, targetUrl);
+        fragment.setArguments(args);
+        return fragment;
+    }
+    
+    
+    public SamlWebViewDialog() {
+        super();
+        Log_OC.d(TAG, "constructor");
+    }
+    
+    
+    @Override
+    public void onAttach(Activity activity) {
+        Log_OC.d(TAG, "onAttach");
+        super.onAttach(activity);
+        try {
+            mSsoWebViewClientListener = (SsoWebViewClientListener) activity;
+            mHandler = new Handler();
+            mWebViewClient = new SsoWebViewClient(mHandler, mSsoWebViewClientListener);
+            
+       } catch (ClassCastException e) {
+            throw new ClassCastException(activity.toString() + " must implement " + SsoWebViewClientListener.class.getSimpleName());
+        }
+    }
+
+    
+    @SuppressLint("SetJavaScriptEnabled")
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        Log_OC.d(TAG, "onCreate");
+        super.onCreate(savedInstanceState);
+        
+        CookieSyncManager.createInstance(getActivity());
+
+        if (savedInstanceState == null) {
+            mInitialUrl = getArguments().getString(ARG_INITIAL_URL);
+            mTargetUrl = getArguments().getString(ARG_TARGET_URL);
+        } else {
+            mInitialUrl = savedInstanceState.getString(ARG_INITIAL_URL);
+            mTargetUrl = savedInstanceState.getString(ARG_TARGET_URL);
+        }
+        
+        setStyle(SherlockDialogFragment.STYLE_NO_TITLE, R.style.Theme_ownCloud_Dialog);
+    }
+    
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        Log_OC.d(TAG, "onCreateDialog");
+
+        /*
+        // build the dialog
+        AlertDialog.Builder builder = new AlertDialog.Builder(getSherlockActivity());
+        if (mSsoRootView.getParent() != null) {
+            ((ViewGroup)(mSsoRootView.getParent())).removeView(mSsoRootView);
+        }
+        builder.setView(mSsoRootView);
+        //builder.setView(mSsoWebView);
+        Dialog dialog = builder.create();
+        */
+        
+        return super.onCreateDialog(savedInstanceState);
+    }
+
+    @SuppressLint("SetJavaScriptEnabled")
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        Log_OC.d(TAG, "onCreateView");
+        
+        // Inflate layout of the dialog  
+        View rootView = inflater.inflate(R.layout.sso_dialog, container, false);  // null parent view because it will go in the dialog layout
+        mSsoWebView  = (WebView) rootView.findViewById(R.id.sso_webview);
+            
+        mWebViewClient.setTargetUrl(mTargetUrl);
+        mSsoWebView.setWebViewClient(mWebViewClient);
+        
+        if (savedInstanceState == null) {
+            Log_OC.d(TAG,  "   initWebView start");
+            CookieManager cookieManager = CookieManager.getInstance();
+            cookieManager.setAcceptCookie(true);
+            cookieManager.removeAllCookie();
+            mSsoWebView.loadUrl(mInitialUrl);
+            
+        } else {
+            Log_OC.d(TAG, "   restoreWebView start");
+            WebBackForwardList history = mSsoWebView.restoreState(savedInstanceState.getBundle(KEY_WEBVIEW_STATE));
+            if (history == null) {
+                Log_OC.e(TAG, "Error restoring WebView state ; back to starting URL");
+                mSsoWebView.loadUrl(mInitialUrl);
+            }
+        }
+
+        WebSettings webSettings = mSsoWebView.getSettings();
+        webSettings.setJavaScriptEnabled(true);
+        webSettings.setBuiltInZoomControls(true);
+        webSettings.setLoadWithOverviewMode(false);
+        webSettings.setSavePassword(false);
+        webSettings.setUserAgentString(WebdavClient.USER_AGENT);
+        webSettings.setSaveFormData(false);
+        
+        return rootView;
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        Log_OC.d(SAML_DIALOG_TAG, "onSaveInstanceState being CALLED");
+        super.onSaveInstanceState(outState);
+        
+        // save URLs
+        outState.putString(ARG_INITIAL_URL, mInitialUrl);
+        outState.putString(ARG_TARGET_URL, mTargetUrl);
+        
+        // Save the state of the WebView
+        Bundle webviewState = new Bundle();
+        mSsoWebView.saveState(webviewState);
+        outState.putBundle(KEY_WEBVIEW_STATE, webviewState);
+    }
+
+    @Override
+    public void onDestroyView() {
+        Log_OC.d(TAG, "onDestroyView");
+        
+        mSsoWebView.setWebViewClient(null);
+        
+        // Work around bug: http://code.google.com/p/android/issues/detail?id=17423
+        Dialog dialog = getDialog();
+        if ((dialog != null)) {
+            dialog.setOnDismissListener(null);
+            //dialog.dismiss();
+            //dialog.setDismissMessage(null);
+        }
+        
+        super.onDestroyView();
+    }
+    
+    @Override
+    public void onDestroy() {
+        Log_OC.d(TAG, "onDestroy");
+        super.onDestroy();
+    }
+
+    @Override
+    public void onDetach() {
+        Log_OC.d(TAG, "onDetach");
+        mSsoWebViewClientListener = null;
+        mWebViewClient = null;
+        super.onDetach();
+    }
+    
+    @Override
+    public void onCancel (DialogInterface dialog) {
+        Log_OC.d(SAML_DIALOG_TAG, "onCancel");
+        super.onCancel(dialog);
+    }
+    
+    @Override
+    public void onDismiss (DialogInterface dialog) {
+        Log_OC.d(SAML_DIALOG_TAG, "onDismiss");
+        super.onDismiss(dialog);
+    }
+    
+    @Override
+    public void onStart() {
+        Log_OC.d(SAML_DIALOG_TAG, "onStart");
+        super.onStart();
+    }
+
+    @Override
+    public void onStop() {
+        Log_OC.d(SAML_DIALOG_TAG, "onStop");
+        super.onStop();
+    }
+
+    @Override
+    public void onResume() {
+        Log_OC.d(SAML_DIALOG_TAG, "onResume");
+        super.onResume();
+    }
+
+    @Override
+    public void onPause() {
+        Log_OC.d(SAML_DIALOG_TAG, "onPause");
+        super.onPause();
+    }
+    
+    @Override
+    public int show (FragmentTransaction transaction, String tag) {
+        Log_OC.d(SAML_DIALOG_TAG, "show (transaction)");
+        return super.show(transaction, tag);
+    }
+
+    @Override
+    public void show (FragmentManager manager, String tag) {
+        Log_OC.d(SAML_DIALOG_TAG, "show (manager)");
+        super.show(manager, tag);
+    }
+
+}
\ No newline at end of file
diff --git a/src/com/owncloud/android/ui/dialog/SslValidatorDialog.java b/src/com/owncloud/android/ui/dialog/SslValidatorDialog.java
new file mode 100644 (file)
index 0000000..82ecd6b
--- /dev/null
@@ -0,0 +1,356 @@
+/* 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.dialog;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.security.auth.x500.X500Principal;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.network.CertificateCombinedException;
+import com.owncloud.android.network.OwnCloudClientUtils;
+import com.owncloud.android.operations.RemoteOperationResult;
+import com.owncloud.android.ui.CustomButton;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.view.Window;
+import android.widget.TextView;
+
+
+/**
+ * Dialog to request the user about a certificate that could not be validated with the certificates store in the system.
+ * 
+ * @author David A. Velasco
+ */
+public class SslValidatorDialog extends Dialog {
+
+    private final static String TAG = SslValidatorDialog.class.getSimpleName();
+
+    private OnSslValidatorListener mListener;
+    private CertificateCombinedException mException = null;
+    private View mView;
+    
+    
+    /**
+     * Creates a new SslValidatorDialog to ask the user if an untrusted certificate from a server should
+     * be trusted.
+     * 
+     * @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. NULL if the operation can not be recovered
+     *                      by setting the certificate as reliable.
+     */
+    public static SslValidatorDialog newInstance(Context context, RemoteOperationResult result, OnSslValidatorListener listener) {
+        if (result != null && result.isSslRecoverableException()) {
+            SslValidatorDialog dialog = new SslValidatorDialog(context, listener);
+            return dialog;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Private constructor. 
+     * 
+     * Instances have to be created through static {@link SslValidatorDialog#newInstance}.
+     * 
+     * @param context       Android context where the dialog will live
+     * @param e             Exception causing the need of prompt the user about the server certificate.
+     * @param listener      Object to notice when the server certificate was added to the local certificates store.
+     */
+    private SslValidatorDialog(Context context, OnSslValidatorListener listener) {
+        super(context);
+        mListener = listener;
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        requestWindowFeature(Window.FEATURE_NO_TITLE);
+        mView = getLayoutInflater().inflate(R.layout.ssl_validator_layout, null);
+        setContentView(mView); 
+        
+        mView.findViewById(R.id.ok).setOnClickListener( 
+                new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        try {
+                            saveServerCert();
+                            dismiss();
+                            if (mListener != null)
+                                mListener.onSavedCertificate();
+                            else
+                                Log_OC.d(TAG, "Nobody there to notify the certificate was saved");
+                            
+                        } catch (GeneralSecurityException e) {
+                            dismiss();
+                            if (mListener != null)
+                                mListener.onFailedSavingCertificate();
+                            Log_OC.e(TAG, "Server certificate could not be saved in the known servers trust store ", e);
+                            
+                        } catch (IOException e) {
+                            dismiss();
+                            if (mListener != null)
+                                mListener.onFailedSavingCertificate();
+                            Log_OC.e(TAG, "Server certificate could not be saved in the known servers trust store ", e);
+                        }
+                    }
+                });
+        
+        mView.findViewById(R.id.cancel).setOnClickListener(
+                new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        cancel();
+                    }
+                });
+        
+        mView.findViewById(R.id.details_btn).setOnClickListener(
+                new View.OnClickListener() {
+                   @Override
+                    public void onClick(View v) {
+                       View detailsScroll = findViewById(R.id.details_scroll);
+                       if (detailsScroll.getVisibility() == View.VISIBLE) {
+                           detailsScroll.setVisibility(View.GONE);
+                           ((CustomButton)v).setText(R.string.ssl_validator_btn_details_see);
+                           
+                       } else {
+                           detailsScroll.setVisibility(View.VISIBLE);
+                           ((CustomButton)v).setText(R.string.ssl_validator_btn_details_hide);
+                       }
+                    }
+                });
+    }
+    
+    
+    public void updateResult(RemoteOperationResult result) {
+        if (result.isSslRecoverableException()) {
+            mException = (CertificateCombinedException) result.getException();
+            
+            /// clean
+            mView.findViewById(R.id.reason_cert_not_trusted).setVisibility(View.GONE);
+            mView.findViewById(R.id.reason_cert_expired).setVisibility(View.GONE);
+            mView.findViewById(R.id.reason_cert_not_yet_valid).setVisibility(View.GONE);
+            mView.findViewById(R.id.reason_hostname_not_verified).setVisibility(View.GONE);
+            mView.findViewById(R.id.details_scroll).setVisibility(View.GONE);
+
+            /// refresh
+            if (mException.getCertPathValidatorException() != null) {
+                ((TextView)mView.findViewById(R.id.reason_cert_not_trusted)).setVisibility(View.VISIBLE);
+            }
+            
+            if (mException.getCertificateExpiredException() != null) {
+                ((TextView)mView.findViewById(R.id.reason_cert_expired)).setVisibility(View.VISIBLE);
+            }
+            
+            if (mException.getCertificateNotYetValidException() != null) {
+                ((TextView)mView.findViewById(R.id.reason_cert_not_yet_valid)).setVisibility(View.VISIBLE);
+            } 
+
+            if (mException.getSslPeerUnverifiedException() != null ) {
+                ((TextView)mView.findViewById(R.id.reason_hostname_not_verified)).setVisibility(View.VISIBLE);
+            }
+            
+            
+            showCertificateData(mException.getServerCertificate());
+        }
+        
+    }
+    
+    private void showCertificateData(X509Certificate cert) {
+
+        if (cert != null) {
+            showSubject(cert.getSubjectX500Principal());
+            showIssuer(cert.getIssuerX500Principal());
+            showValidity(cert.getNotBefore(), cert.getNotAfter());
+            showSignature(cert);
+            
+        } else {
+            // this should not happen
+            // TODO
+        }
+    }
+
+    private void showSignature(X509Certificate cert) {
+        TextView sigView = ((TextView)mView.findViewById(R.id.value_signature));
+        TextView algorithmView = ((TextView)mView.findViewById(R.id.value_signature_algorithm));
+        sigView.setText(getHex(cert.getSignature()));
+        algorithmView.setText(cert.getSigAlgName());
+    }
+    
+    public String getHex(final byte [] raw) {
+        if (raw == null) {
+           return null;
+        }
+        final StringBuilder hex = new StringBuilder(2 * raw.length);
+        for (final byte b : raw) {
+           final int hiVal = (b & 0xF0) >> 4;
+           final int loVal = b & 0x0F;
+           hex.append((char) ('0' + (hiVal + (hiVal / 10 * 7))));
+           hex.append((char) ('0' + (loVal + (loVal / 10 * 7))));
+        }
+        return hex.toString();
+     }    
+
+    private void showValidity(Date notBefore, Date notAfter) {
+        TextView fromView = ((TextView)mView.findViewById(R.id.value_validity_from));
+        TextView toView = ((TextView)mView.findViewById(R.id.value_validity_to));
+        fromView.setText(notBefore.toLocaleString());
+        toView.setText(notAfter.toLocaleString());
+    }
+
+    private void showSubject(X500Principal subject) {
+        Map<String, String> s = parsePrincipal(subject);
+        TextView cnView = ((TextView)mView.findViewById(R.id.value_subject_CN));
+        TextView oView = ((TextView)mView.findViewById(R.id.value_subject_O));
+        TextView ouView = ((TextView)mView.findViewById(R.id.value_subject_OU));
+        TextView cView = ((TextView)mView.findViewById(R.id.value_subject_C));
+        TextView stView = ((TextView)mView.findViewById(R.id.value_subject_ST));
+        TextView lView = ((TextView)mView.findViewById(R.id.value_subject_L));
+        
+        if (s.get("CN") != null) {
+            cnView.setText(s.get("CN"));
+            cnView.setVisibility(View.VISIBLE);
+        } else {
+            cnView.setVisibility(View.GONE);
+        }
+        if (s.get("O") != null) {
+            oView.setText(s.get("O"));
+            oView.setVisibility(View.VISIBLE);
+        } else {
+            oView.setVisibility(View.GONE);
+        }
+        if (s.get("OU") != null) {
+            ouView.setText(s.get("OU"));
+            ouView.setVisibility(View.VISIBLE);
+        } else {
+            ouView.setVisibility(View.GONE);
+        }
+        if (s.get("C") != null) {
+            cView.setText(s.get("C"));
+            cView.setVisibility(View.VISIBLE);
+        } else {
+            cView.setVisibility(View.GONE);
+        }
+        if (s.get("ST") != null) {
+            stView.setText(s.get("ST"));
+            stView.setVisibility(View.VISIBLE);
+        } else {
+            stView.setVisibility(View.GONE);
+        }
+        if (s.get("L") != null) {
+            lView.setText(s.get("L"));
+            lView.setVisibility(View.VISIBLE);
+        } else {
+            lView.setVisibility(View.GONE);
+        }
+    }
+    
+    private void showIssuer(X500Principal issuer) {
+        Map<String, String> s = parsePrincipal(issuer);
+        TextView cnView = ((TextView)mView.findViewById(R.id.value_issuer_CN));
+        TextView oView = ((TextView)mView.findViewById(R.id.value_issuer_O));
+        TextView ouView = ((TextView)mView.findViewById(R.id.value_issuer_OU));
+        TextView cView = ((TextView)mView.findViewById(R.id.value_issuer_C));
+        TextView stView = ((TextView)mView.findViewById(R.id.value_issuer_ST));
+        TextView lView = ((TextView)mView.findViewById(R.id.value_issuer_L));
+        
+        if (s.get("CN") != null) {
+            cnView.setText(s.get("CN"));
+            cnView.setVisibility(View.VISIBLE);
+        } else {
+            cnView.setVisibility(View.GONE);
+        }
+        if (s.get("O") != null) {
+            oView.setText(s.get("O"));
+            oView.setVisibility(View.VISIBLE);
+        } else {
+            oView.setVisibility(View.GONE);
+        }
+        if (s.get("OU") != null) {
+            ouView.setText(s.get("OU"));
+            ouView.setVisibility(View.VISIBLE);
+        } else {
+            ouView.setVisibility(View.GONE);
+        }
+        if (s.get("C") != null) {
+            cView.setText(s.get("C"));
+            cView.setVisibility(View.VISIBLE);
+        } else {
+            cView.setVisibility(View.GONE);
+        }
+        if (s.get("ST") != null) {
+            stView.setText(s.get("ST"));
+            stView.setVisibility(View.VISIBLE);
+        } else {
+            stView.setVisibility(View.GONE);
+        }
+        if (s.get("L") != null) {
+            lView.setText(s.get("L"));
+            lView.setVisibility(View.VISIBLE);
+        } else {
+            lView.setVisibility(View.GONE);
+        }
+    }
+    
+
+    private Map<String, String> parsePrincipal(X500Principal principal) {
+        Map<String, String> result = new HashMap<String, String>();
+        String toParse = principal.getName();
+        String[] pieces = toParse.split(",");
+        String[] tokens = {"CN", "O", "OU", "C", "ST", "L"}; 
+        for (int i=0; i < pieces.length ; i++) {
+            for (int j=0; j<tokens.length; j++) {
+                if (pieces[i].startsWith(tokens[j] + "=")) {
+                    result.put(tokens[j], pieces[i].substring(tokens[j].length()+1));
+                }
+            }
+        }
+        return result;
+    }
+
+    private void saveServerCert() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
+        if (mException.getServerCertificate() != null) {
+            // TODO make this asynchronously, it can take some time
+            OwnCloudClientUtils.addCertToKnownServersStore(mException.getServerCertificate(), getContext());
+        }
+    }
+
+    
+    public interface OnSslValidatorListener {
+        public void onSavedCertificate();
+        public void onFailedSavingCertificate();
+    }
+}
+
diff --git a/src/com/owncloud/android/ui/dialog/SsoWebView.java b/src/com/owncloud/android/ui/dialog/SsoWebView.java
new file mode 100644 (file)
index 0000000..3a71139
--- /dev/null
@@ -0,0 +1,40 @@
+/* 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.dialog;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.webkit.WebView;
+
+public class SsoWebView extends WebView {
+    
+    public SsoWebView(Context context) {
+        super(context);
+    }
+    
+    public SsoWebView(Context context, AttributeSet attr) {
+        super(context, attr);
+    }
+    
+    @Override
+    public boolean onCheckIsTextEditor () {
+        return false;
+    }
+    
+}
+
diff --git a/src/com/owncloud/android/ui/fragment/AuthenticatorAccountDetailsFragment.java b/src/com/owncloud/android/ui/fragment/AuthenticatorAccountDetailsFragment.java
new file mode 100644 (file)
index 0000000..f096103
--- /dev/null
@@ -0,0 +1,25 @@
+/* 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.ui.fragment;
+
+import com.actionbarsherlock.app.SherlockFragment;
+
+public class AuthenticatorAccountDetailsFragment extends SherlockFragment {
+
+}
diff --git a/src/com/owncloud/android/ui/fragment/AuthenticatorGetStartedFragment.java b/src/com/owncloud/android/ui/fragment/AuthenticatorGetStartedFragment.java
new file mode 100644 (file)
index 0000000..5abf55d
--- /dev/null
@@ -0,0 +1,25 @@
+/* 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.ui.fragment;
+
+import com.actionbarsherlock.app.SherlockFragment;
+
+public class AuthenticatorGetStartedFragment extends SherlockFragment {
+
+}
diff --git a/src/com/owncloud/android/ui/fragment/ConfirmationDialogFragment.java b/src/com/owncloud/android/ui/fragment/ConfirmationDialogFragment.java
new file mode 100644 (file)
index 0000000..869cb26
--- /dev/null
@@ -0,0 +1,126 @@
+/* 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.ui.fragment;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+import com.actionbarsherlock.app.SherlockDialogFragment;
+import com.owncloud.android.Log_OC;
+
+
+public class ConfirmationDialogFragment extends SherlockDialogFragment {
+
+    public final static String ARG_CONF_RESOURCE_ID = "resource_id";
+    public final static String ARG_CONF_ARGUMENTS = "string_array";
+    
+    public final static String ARG_POSITIVE_BTN_RES = "positive_btn_res";
+    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;
+    
+    /**
+     * Public factory method to create new ConfirmationDialogFragment instances.
+     * 
+     * @param string_id         Resource id for a message to show in the dialog.
+     * @param arguments         Arguments to complete the message, if it's a format string.
+     * @param posBtn            Resource id for the text of the positive button.
+     * @param neuBtn            Resource id for the text of the neutral button.
+     * @param negBtn            Resource id for the text of the negative button.
+     * @return                  Dialog ready to show.
+     */
+    public static ConfirmationDialogFragment newInstance(int string_id, String[] arguments, int posBtn, int neuBtn, int negBtn) {
+        ConfirmationDialogFragment frag = new ConfirmationDialogFragment();
+        Bundle args = new Bundle();
+        args.putInt(ARG_CONF_RESOURCE_ID, string_id);
+        args.putStringArray(ARG_CONF_ARGUMENTS, arguments);
+        args.putInt(ARG_POSITIVE_BTN_RES, posBtn);
+        args.putInt(ARG_NEUTRAL_BTN_RES, neuBtn);
+        args.putInt(ARG_NEGATIVE_BTN_RES, negBtn);
+        frag.setArguments(args);
+        return frag;
+    }
+    
+    public void setOnConfirmationListener(ConfirmationDialogFragmentListener listener) {
+        mListener = listener;
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        Object[] confirmationTarget = getArguments().getStringArray(ARG_CONF_ARGUMENTS);
+        int resourceId = getArguments().getInt(ARG_CONF_RESOURCE_ID, -1);
+        int posBtn = getArguments().getInt(ARG_POSITIVE_BTN_RES, -1);
+        int neuBtn = getArguments().getInt(ARG_NEUTRAL_BTN_RES, -1);
+        int negBtn = getArguments().getInt(ARG_NEGATIVE_BTN_RES, -1);
+        
+        if (confirmationTarget == null || resourceId == -1) {
+            Log_OC.wtf(getTag(), "Calling confirmation dialog without resource or arguments");
+            return null;
+        }
+
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
+            .setIcon(android.R.drawable.ic_dialog_alert)
+            .setMessage(String.format(getString(resourceId), confirmationTarget))
+            .setTitle(android.R.string.dialog_alert_title);
+        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
+            builder.setIconAttribute(android.R.attr.alertDialogIcon);
+        }
+        
+        if (posBtn != -1)
+            builder.setPositiveButton(posBtn,
+                    new DialogInterface.OnClickListener() {
+                        public void onClick(DialogInterface dialog, int whichButton) {
+                            mListener.onConfirmation(getTag()); 
+                            dialog.dismiss();
+                        }
+                    });
+        if (neuBtn != -1)
+            builder.setNeutralButton(neuBtn,
+                    new DialogInterface.OnClickListener() {
+                        public void onClick(DialogInterface dialog, int whichButton) {
+                            mListener.onNeutral(getTag()); 
+                            dialog.dismiss();
+                        }
+                    });
+        if (negBtn != -1)
+            builder.setNegativeButton(negBtn,
+                    new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int which) {
+                            mListener.onCancel(getTag());
+                            dialog.dismiss();
+                        }
+                    });
+      return builder.create();
+    }
+    
+    
+    public interface ConfirmationDialogFragmentListener {
+        public void onConfirmation(String callerTag);
+        public void onNeutral(String callerTag);
+        public void onCancel(String callerTag);
+    }
+    
+}
+
diff --git a/src/com/owncloud/android/ui/fragment/ExtendedListFragment.java b/src/com/owncloud/android/ui/fragment/ExtendedListFragment.java
new file mode 100644 (file)
index 0000000..a57fb09
--- /dev/null
@@ -0,0 +1,119 @@
+/* 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.ui.fragment;
+
+import com.actionbarsherlock.app.SherlockFragment;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.ui.ExtendedListView;
+
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ListAdapter;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ListView;
+
+/**
+ *  TODO extending SherlockListFragment instead of SherlockFragment 
+ */
+public class ExtendedListFragment extends SherlockFragment implements OnItemClickListener {
+    
+    private static final String TAG = ExtendedListFragment.class.getSimpleName();
+
+    private static final String KEY_SAVED_LIST_POSITION = "SAVED_LIST_POSITION"; 
+
+    protected ExtendedListView mList;
+    
+    public void setListAdapter(ListAdapter listAdapter) {
+        mList.setAdapter(listAdapter);
+        mList.invalidate();
+    }
+
+    public ListView getListView() {
+        return mList;
+    }
+    
+    
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        Log_OC.e(TAG, "onCreateView");
+        //mList = new ExtendedListView(getActivity());
+        View v = inflater.inflate(R.layout.list_fragment, null);
+        mList = (ExtendedListView)(v.findViewById(R.id.list_root));
+        mList.setOnItemClickListener(this);
+        //mList.setEmptyView(v.findViewById(R.id.empty_list_view));     // looks like it's not a cool idea 
+        mList.setDivider(getResources().getDrawable(R.drawable.uploader_list_separator));
+        mList.setDividerHeight(1);
+
+        if (savedInstanceState != null) {
+            int referencePosition = savedInstanceState.getInt(KEY_SAVED_LIST_POSITION);
+            setReferencePosition(referencePosition);
+        }
+        
+        return v;
+    }
+
+    
+    @Override
+    public void onSaveInstanceState(Bundle savedInstanceState) {
+        super.onSaveInstanceState(savedInstanceState);
+        Log_OC.e(TAG, "onSaveInstanceState()");
+        savedInstanceState.putInt(KEY_SAVED_LIST_POSITION, getReferencePosition());
+    }
+
+    
+    /**
+     * Calculates the position of the item that will be used as a reference to reposition the visible items in the list when
+     * the device is turned to other position. 
+     * 
+     * THe current policy is take as a reference the visible item in the center of the screen.  
+     * 
+     * @return      The position in the list of the visible item in the center of the screen.
+     */
+    protected int getReferencePosition() {
+        if (mList != null) {
+            return (mList.getFirstVisiblePosition() + mList.getLastVisiblePosition()) / 2;
+        } else {
+            return 0;
+        }
+    }
+
+    
+    /**
+     * Sets the visible part of the list from the reference position.
+     * 
+     * @param   position    Reference position previously returned by {@link LocalFileListFragment#getReferencePosition()}
+     */
+    protected void setReferencePosition(int position) {
+        if (mList != null) {
+            mList.setAndCenterSelection(position);
+        }
+    }
+
+    @Override
+    public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
+        // to be @overriden  
+    }
+
+    
+}
diff --git a/src/com/owncloud/android/ui/fragment/FileDetailFragment.java b/src/com/owncloud/android/ui/fragment/FileDetailFragment.java
new file mode 100644 (file)
index 0000000..36d7c21
--- /dev/null
@@ -0,0 +1,930 @@
+/* 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 java.util.ArrayList;
+import java.util.List;
+
+import android.accounts.Account;
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+import com.owncloud.android.DisplayUtils;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+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.RemoveFileOperation;
+import com.owncloud.android.operations.RenameFileOperation;
+import com.owncloud.android.operations.SynchronizeFileOperation;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.ui.activity.ConflictsResolveActivity;
+import com.owncloud.android.ui.activity.FileActivity;
+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.ui.preview.PreviewImageFragment;
+
+
+import eu.alefzero.webdav.OnDatatransferProgressListener;
+
+/**
+ * This Fragment is used to display the details about a file.
+ * 
+ * @author Bartek Przybylski
+ * @author David A. Velasco
+ */
+public class FileDetailFragment extends FileFragment implements
+        OnClickListener, 
+        ConfirmationDialogFragment.ConfirmationDialogFragmentListener, OnRemoteOperationListener, EditNameDialogListener {
+
+    private FileFragment.ContainerActivity mContainerActivity;
+    
+    private int mLayout;
+    private View mView;
+    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_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() {
+        super();
+        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) {
+        super(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();
+        setHasOptionsMenu(true);
+    }
+    
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        //super.onCreateView(inflater, container, savedInstanceState);
+        
+        if (savedInstanceState != null) {
+            setFile((OCFile)savedInstanceState.getParcelable(FileActivity.EXTRA_FILE));
+            mAccount = savedInstanceState.getParcelable(FileActivity.EXTRA_ACCOUNT);
+        }
+        
+        if(getFile() != null && mAccount != null) {
+            mLayout = R.layout.file_details_fragment;
+        }
+        
+        View view = null;
+        //view = inflater.inflate(mLayout, container, false);
+        view = inflater.inflate(mLayout, null);
+        mView = view;
+        
+        if (mLayout == R.layout.file_details_fragment) {
+            mView.findViewById(R.id.fdKeepInSync).setOnClickListener(this);
+            ProgressBar progressBar = (ProgressBar)mView.findViewById(R.id.fdProgressBar);
+            mProgressListener = new ProgressListener(progressBar);
+            mView.findViewById(R.id.fdCancelBtn).setOnClickListener(this);
+        }
+        
+        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(FileActivity.EXTRA_FILE, getFile());
+        outState.putParcelable(FileActivity.EXTRA_ACCOUNT, mAccount);
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        listenForTransferProgress();
+    }
+    
+    @Override
+    public void onResume() {
+        super.onResume();
+        mUploadFinishReceiver = new UploadFinishReceiver();
+        FileUploader fileUploader = new FileUploader();
+        IntentFilter filter = new IntentFilter(fileUploader.getUploadFinishMessage());
+        getActivity().registerReceiver(mUploadFinishReceiver, filter);
+
+    }
+
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        if (mUploadFinishReceiver != null) {
+            getActivity().unregisterReceiver(mUploadFinishReceiver);
+            mUploadFinishReceiver = null;
+        }
+    }
+
+    
+    @Override
+    public void onStop() {
+        super.onStop();
+        leaveTransferProgress();
+    }
+
+    
+    @Override
+    public View getView() {
+        return super.getView() == null ? mView : super.getView();
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        super.onCreateOptionsMenu(menu, inflater);
+        inflater.inflate(R.menu.file_actions_menu, menu);
+        MenuItem item = menu.findItem(R.id.action_see_details);
+        if (item != null) {
+            item.setVisible(false);
+            item.setEnabled(false);
+        }
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onPrepareOptionsMenu (Menu menu) {
+        super.onPrepareOptionsMenu(menu);
+        
+        List<Integer> toHide = new ArrayList<Integer>();
+        List<Integer> toShow = new ArrayList<Integer>();
+        OCFile file = getFile();
+        
+        FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
+        boolean downloading = downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file);
+        FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
+        boolean uploading = uploaderBinder != null && uploaderBinder.isUploading(mAccount, getFile());
+        
+        if (downloading || uploading) {
+            toHide.add(R.id.action_download_file);
+            toHide.add(R.id.action_rename_file);
+            toHide.add(R.id.action_remove_file);
+            toHide.add(R.id.action_open_file_with);
+            if (!downloading) {
+                toHide.add(R.id.action_cancel_download);
+                toShow.add(R.id.action_cancel_upload);
+            } else {
+                toHide.add(R.id.action_cancel_upload);
+                toShow.add(R.id.action_cancel_download);
+            }
+
+        } else if (file != null && file.isDown()) {
+            toHide.add(R.id.action_download_file);
+            toHide.add(R.id.action_cancel_download);
+            toHide.add(R.id.action_cancel_upload);
+            
+            toShow.add(R.id.action_rename_file);
+            toShow.add(R.id.action_remove_file);
+            toShow.add(R.id.action_open_file_with);
+            toShow.add(R.id.action_sync_file);
+            
+        } else if (file != null) {
+            toHide.add(R.id.action_open_file_with);
+            toHide.add(R.id.action_cancel_download);
+            toHide.add(R.id.action_cancel_upload);
+            toHide.add(R.id.action_sync_file);
+            
+            toShow.add(R.id.action_rename_file);
+            toShow.add(R.id.action_remove_file);
+            toShow.add(R.id.action_download_file);
+            
+        } else {
+            toHide.add(R.id.action_open_file_with);
+            toHide.add(R.id.action_cancel_download);
+            toHide.add(R.id.action_cancel_upload);
+            toHide.add(R.id.action_sync_file);
+            toHide.add(R.id.action_download_file);
+            toHide.add(R.id.action_rename_file);
+            toHide.add(R.id.action_remove_file);
+            
+        }
+
+        MenuItem item = null;
+        for (int i : toHide) {
+            item = menu.findItem(i);
+            if (item != null) {
+                item.setVisible(false);
+                item.setEnabled(false);
+            }
+        }
+        for (int i : toShow) {
+            item = menu.findItem(i);
+            if (item != null) {
+                item.setVisible(true);
+                item.setEnabled(true);
+            }
+        }
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.action_open_file_with: {
+                mContainerActivity.openFile(getFile());
+                return true;
+            }
+            case R.id.action_remove_file: {
+                removeFile();
+                return true;
+            }
+            case R.id.action_rename_file: {
+                renameFile();
+                return true;
+            }
+            case R.id.action_download_file: 
+            case R.id.action_cancel_download:
+            case R.id.action_cancel_upload:
+            case R.id.action_sync_file: {
+                synchronizeFile();
+                return true;
+            }
+            default:
+                return false;
+        }
+    }
+    
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.fdKeepInSync: {
+                toggleKeepInSync();
+                break;
+            }
+            case R.id.fdCancelBtn: {
+                synchronizeFile();
+                break;
+            }
+            default:
+                Log_OC.e(TAG, "Incorrect view clicked!");
+        }
+    }
+    
+    
+    private void toggleKeepInSync() {
+        CheckBox cb = (CheckBox) getView().findViewById(R.id.fdKeepInSync);
+        OCFile file = getFile();
+        file.setKeepInSync(cb.isChecked());
+        mStorageManager.saveFile(file);
+        
+        /// 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, file);
+        intent.putExtra(FileObserverService.KEY_CMD_ARG_ACCOUNT, mAccount);
+        getActivity().startService(intent);
+        
+        if (file.keepInSync()) {
+            synchronizeFile();   // force an immediate synchronization
+        }
+    }
+
+
+    private void removeFile() {
+        OCFile file = getFile();
+        ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
+                R.string.confirmation_remove_alert,
+                new String[]{file.getFileName()},
+                file.isDown() ? R.string.confirmation_remove_remote_and_local : R.string.confirmation_remove_remote,
+                file.isDown() ? R.string.confirmation_remove_local : -1,
+                R.string.common_cancel);
+        confDialog.setOnConfirmationListener(this);
+        confDialog.show(getFragmentManager(), FTAG_CONFIRMATION);
+    }
+
+
+    private void renameFile() {
+        OCFile file = getFile();
+        String fileName = file.getFileName();
+        int extensionStart = file.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");
+    }
+
+    private void synchronizeFile() {
+        OCFile file = getFile();
+        FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
+        FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
+        if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) {
+            downloaderBinder.cancel(mAccount, file);
+            if (file.isDown()) {
+                setButtonsForDown();
+            } else {
+                setButtonsForRemote();
+            }
+
+        } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file)) {
+            uploaderBinder.cancel(mAccount, file);
+            if (!file.fileExists()) {
+                // TODO make something better
+                ((FileDisplayActivity)getActivity()).cleanSecondFragment();
+                
+            } else if (file.isDown()) {
+                setButtonsForDown();
+            } else {
+                setButtonsForRemote();
+            }
+            
+        } else {
+            mLastRemoteOperation = new SynchronizeFileOperation(file, null, mStorageManager, mAccount, true, false, getActivity());
+            mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
+            
+            // update ui 
+            ((FileDisplayActivity) getActivity()).showLoadingDialog();
+            
+        }
+    }
+
+    @Override
+    public void onConfirmation(String callerTag) {
+        OCFile file = getFile();
+        if (callerTag.equals(FTAG_CONFIRMATION)) {
+            if (mStorageManager.getFileById(file.getFileId()) != null) {
+                mLastRemoteOperation = new RemoveFileOperation( file, 
+                                                                true, 
+                                                                mStorageManager);
+                mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
+                                
+                ((FileDisplayActivity) getActivity()).showLoadingDialog();
+            }
+        }
+    }
+    
+    @Override
+    public void onNeutral(String callerTag) {
+        File f = null;
+        OCFile file = getFile();
+        if (file.isDown() && (f = new File(file.getStoragePath())).exists()) {
+            f.delete();
+            file.setStoragePath(null);
+            mStorageManager.saveFile(file);
+            updateFileDetails(file, 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 || getFile() == null || mAccount == null);
+    }
+
+    
+    /**
+     * Use this method to signal this Activity that it shall update its view.
+     * 
+     * @param file : An {@link OCFile}
+     */
+    public void updateFileDetails(OCFile file, Account ocAccount) {
+        setFile(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) {
+                setFile(mStorageManager.getFileByPath(getFile().getRemotePath()));
+            }
+            OCFile file = getFile();
+            
+            // set file details
+            setFilename(file.getFileName());
+            setFiletype(file.getMimetype());
+            setFilesize(file.getFileLength());
+            if(ocVersionSupportsTimeCreated()){
+                setTimeCreated(file.getCreationTimestamp());
+            }
+           
+            setTimeModified(file.getModificationTimestamp());
+            
+            CheckBox cb = (CheckBox)getView().findViewById(R.id.fdKeepInSync);
+            cb.setChecked(file.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, file)) || (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file))) {
+                setButtonsForTransferring();
+                
+            } else if (file.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 (getFile() != null && mAccount != null && mLayout == R.layout.file_details_fragment);        
+    }
+
+
+    /**
+     * Updates the filename in view
+     * @param filename to set
+     */
+    private void setFilename(String filename) {
+        TextView tv = (TextView) getView().findViewById(R.id.fdFilename);
+        if (tv != null)
+            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()) {
+            // let's protect the user from himself ;)
+            getView().findViewById(R.id.fdKeepInSync).setEnabled(false);
+            
+            // show the progress bar for the transfer
+            getView().findViewById(R.id.fdProgressBlock).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, getFile())) {
+                progressText.setText(R.string.downloader_download_in_progress_ticker);
+            } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, getFile())) {
+                progressText.setText(R.string.uploader_upload_in_progress_ticker);
+            }
+        }
+    }
+
+    /**
+     * Enables or disables buttons for a file locally available 
+     */
+    private void setButtonsForDown() {
+        if (!isEmpty()) {
+            getView().findViewById(R.id.fdKeepInSync).setEnabled(true);
+            
+            // hides the progress bar
+            getView().findViewById(R.id.fdProgressBlock).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()) {
+            getView().findViewById(R.id.fdKeepInSync).setEnabled(true);
+            
+            // hides the progress bar
+            getView().findViewById(R.id.fdProgressBlock).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 = getFile().getRemotePath().equals(intent.getStringExtra(FileUploader.EXTRA_OLD_REMOTE_PATH));
+                if (getFile().getRemotePath().equals(uploadRemotePath) ||
+                    renamedInUpload) {
+                    if (uploadWasFine) {
+                        setFile(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
+                   
+                    // Force the preview if the file is an image
+                    if (uploadWasFine && PreviewImageFragment.canBePreviewed(getFile())) {
+                        ((FileDisplayActivity) mContainerActivity).startImagePreview(getFile());
+                    } 
+                }
+            }
+        }
+    }
+    
+
+    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( getFile(), 
+                                                            mAccount, 
+                                                            newFilename, 
+                                                            new FileDataStorageManager(mAccount, getActivity().getContentResolver()));
+            mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
+            ((FileDisplayActivity) getActivity()).showLoadingDialog();
+        }
+    }
+    
+    
+    /**
+     * {@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) {
+        ((FileDisplayActivity) getActivity()).dismissLoadingDialog();
+        if (result.isSuccess()) {
+            Toast msg = Toast.makeText(getActivity().getApplicationContext(), R.string.remove_success_msg, Toast.LENGTH_LONG);
+            msg.show();
+            ((FileDisplayActivity)getActivity()).cleanSecondFragment();
+
+        } 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) {
+        ((FileDisplayActivity) getActivity()).dismissLoadingDialog();
+        
+        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) {
+        ((FileDisplayActivity) getActivity()).dismissLoadingDialog();
+        OCFile file = getFile();
+        if (!result.isSuccess()) {
+            if (result.getCode() == ResultCode.SYNC_CONFLICT) {
+                Intent i = new Intent(getActivity(), ConflictsResolveActivity.class);
+                i.putExtra(ConflictsResolveActivity.EXTRA_FILE, file);
+                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 (file.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 (file.isDown()) {
+                    setButtonsForDown();
+                    
+                } else {
+                    setButtonsForRemote();
+                }
+            }
+        }
+    }
+    
+
+    public void listenForTransferProgress() {
+        if (mProgressListener != null) {
+            if (mContainerActivity.getFileDownloaderBinder() != null) {
+                mContainerActivity.getFileDownloaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, getFile());
+            }
+            if (mContainerActivity.getFileUploaderBinder() != null) {
+                mContainerActivity.getFileUploaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, getFile());
+            }
+        }
+    }
+    
+    
+    public void leaveTransferProgress() {
+        if (mProgressListener != null) {
+            if (mContainerActivity.getFileDownloaderBinder() != null) {
+                mContainerActivity.getFileDownloaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, getFile());
+            }
+            if (mContainerActivity.getFileUploaderBinder() != null) {
+                mContainerActivity.getFileUploaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, getFile());
+            }
+        }
+    }
+
+
+    
+    /**
+     * 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;
+        }
+
+    };
+
+}
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..b6c6574
--- /dev/null
@@ -0,0 +1,102 @@
+/* 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.support.v4.app.Fragment;
+
+import com.actionbarsherlock.app.SherlockFragment;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.FileHandler;
+import com.owncloud.android.ui.activity.TransferServiceGetter;
+
+
+/**
+ * Common methods for {@link Fragment}s containing {@link OCFile}s
+ * 
+ * @author David A. Velasco
+ *
+ */
+public class FileFragment extends SherlockFragment {
+    
+    private OCFile mFile;
+
+
+    /**
+     * Creates an empty fragment.
+     * 
+     * It's necessary to keep a public constructor without parameters; the system uses it when tries to reinstantiate a fragment automatically. 
+     */
+    public FileFragment() {
+        mFile = null;
+    }
+    
+    /**
+     * Creates an instance for a given {@OCFile}.
+     * 
+     * @param file
+     */
+    public FileFragment(OCFile file) {
+        mFile = file;
+    }
+
+    /**
+     * Getter for the hold {@link OCFile}
+     * 
+     * @return The {@link OCFile} hold
+     */
+    public OCFile getFile() {
+        return mFile;
+    }
+    
+    
+    protected void setFile(OCFile file) {
+        mFile = file;
+    }
+
+    /**
+     * Interface to implement by any Activity that includes some instance of FileFragment
+     * 
+     * @author David A. Velasco
+     */
+    public interface ContainerActivity extends TransferServiceGetter, FileHandler {
+
+        /**
+         * 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 showDetails(OCFile file);
+        
+        
+    }
+    
+}
diff --git a/src/com/owncloud/android/ui/fragment/LandingPageFragment.java b/src/com/owncloud/android/ui/fragment/LandingPageFragment.java
new file mode 100644 (file)
index 0000000..63a95fd
--- /dev/null
@@ -0,0 +1,58 @@
+/* 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 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
+package com.owncloud.android.ui.fragment;\r
+\r
+import com.actionbarsherlock.app.SherlockFragment;\r
+import com.owncloud.android.R;\r
+import com.owncloud.android.ui.activity.LandingActivity;\r
+import com.owncloud.android.ui.adapter.LandingScreenAdapter;\r
+\r
+import android.os.Bundle;\r
+import android.view.LayoutInflater;\r
+import android.view.View;\r
+import android.view.ViewGroup;\r
+import android.widget.ListView;\r
+\r
+/**\r
+ * Used on the Landing page to display what Components of the ownCloud there\r
+ * are. Like Files, Music, Contacts, etc.\r
+ * \r
+ * @author Lennart Rosam\r
+ * \r
+ */\r
+public class LandingPageFragment extends SherlockFragment {\r
+\r
+    @Override\r
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,\r
+            Bundle savedInstanceState) {\r
+        View root = inflater.inflate(R.layout.landing_page_fragment, container);\r
+        return root;\r
+    }\r
+\r
+    @Override\r
+    public void onActivityCreated(Bundle savedInstanceState) {\r
+        super.onActivityCreated(savedInstanceState);\r
+\r
+        ListView landingScreenItems = (ListView) getView().findViewById(\r
+                R.id.homeScreenList);\r
+        landingScreenItems.setAdapter(new LandingScreenAdapter(getActivity()));\r
+        landingScreenItems\r
+                .setOnItemClickListener((LandingActivity) getActivity());\r
+    }\r
+\r
+}\r
diff --git a/src/com/owncloud/android/ui/fragment/LocalFileListFragment.java b/src/com/owncloud/android/ui/fragment/LocalFileListFragment.java
new file mode 100644 (file)
index 0000000..40c03a4
--- /dev/null
@@ -0,0 +1,251 @@
+/* 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 com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.ui.adapter.LocalFileListAdapter;
+
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Environment;
+import android.util.SparseBooleanArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ImageView;
+import android.widget.ListView;
+
+
+/**
+ * A Fragment that lists all files and folders in a given LOCAL path.
+ * 
+ * @author David A. Velasco
+ * 
+ */
+public class LocalFileListFragment extends ExtendedListFragment {
+    private static final String TAG = "LocalFileListFragment";
+    
+    /** Reference to the Activity which this fragment is attached to. For callbacks */
+    private LocalFileListFragment.ContainerActivity mContainerActivity;
+    
+    /** Directory to show */
+    private File mDirectory = null;
+    
+    /** Adapter to connect the data from the directory with the View object */
+    private LocalFileListAdapter mAdapter = null;
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        try {
+            mContainerActivity = (ContainerActivity) activity;
+        } catch (ClassCastException e) {
+            throw new ClassCastException(activity.toString() + " must implement " + LocalFileListFragment.ContainerActivity.class.getSimpleName());
+        }
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        Log_OC.i(TAG, "onCreateView() start");
+        View v = super.onCreateView(inflater, container, savedInstanceState);
+        getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+        Log_OC.i(TAG, "onCreateView() end");
+        return v;
+    }    
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        Log_OC.i(TAG, "onActivityCreated() start");
+        
+        super.onCreate(savedInstanceState);
+        mAdapter = new LocalFileListAdapter(mContainerActivity.getInitialDirectory(), getActivity());
+        setListAdapter(mAdapter);
+        
+        Log_OC.i(TAG, "onActivityCreated() stop");
+    }
+    
+    
+    /**
+     * Checks the file clicked over. Browses inside if it is a directory. Notifies the container activity in any case.
+     */
+    @Override
+    public void onItemClick(AdapterView<?> l, View v, int position, long id) {
+        File file = (File) mAdapter.getItem(position); 
+        if (file != null) {
+            /// Click on a directory
+            if (file.isDirectory()) {
+                // just local updates
+                listDirectory(file);
+                // notify the click to container Activity
+                mContainerActivity.onDirectoryClick(file);
+            
+            } else {    /// Click on a file
+                ImageView checkBoxV = (ImageView) v.findViewById(R.id.custom_checkbox);
+                if (checkBoxV != null) {
+                    if (getListView().isItemChecked(position)) {
+                        checkBoxV.setImageResource(android.R.drawable.checkbox_on_background);
+                    } else {
+                        checkBoxV.setImageResource(android.R.drawable.checkbox_off_background);
+                    }
+                }
+                // notify the change to the container Activity
+                mContainerActivity.onFileClick(file);
+            }
+            
+        } else {
+            Log_OC.w(TAG, "Null object in ListAdapter!!");
+        }
+    }
+
+    
+    /**
+     * Call this, when the user presses the up button
+     */
+    public void onNavigateUp() {
+        File parentDir = null;
+        if(mDirectory != null) {
+            parentDir = mDirectory.getParentFile();  // can be null
+        }
+        listDirectory(parentDir);
+    }
+
+    
+    /**
+     * Use this to query the {@link File} object for the directory
+     * that is currently being displayed by this fragment
+     * 
+     * @return File     The currently displayed directory
+     */
+    public File getCurrentDirectory(){
+        return mDirectory;
+    }
+    
+    
+    /**
+     * Calls {@link LocalFileListFragment#listDirectory(File)} with a null parameter
+     * to refresh the current directory.
+     */
+    public void listDirectory(){
+        listDirectory(null);
+    }
+    
+    
+    /**
+     * Lists the given directory on the view. When the input parameter is null,
+     * it will either refresh the last known directory. list the root
+     * if there never was a directory.
+     * 
+     * @param directory     Directory to be listed
+     */
+    public void listDirectory(File directory) {
+        
+        // Check input parameters for null
+        if(directory == null) {
+            if(mDirectory != null){
+                directory = mDirectory;
+            } else {
+                directory = Environment.getExternalStorageDirectory();  // TODO be careful with the state of the storage; could not be available
+                if (directory == null) return; // no files to show
+            }
+        }
+        
+        
+        // if that's not a directory -> List its parent
+        if(!directory.isDirectory()){
+            Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
+            directory = directory.getParentFile();
+        }
+
+        mList.clearChoices();   // by now, only files in the same directory will be kept as selected
+        mAdapter.swapDirectory(directory);
+        if (mDirectory == null || !mDirectory.equals(directory)) {
+            mList.setSelectionFromTop(0, 0);
+        }
+        mDirectory = directory;
+    }
+    
+
+    /**
+     * Returns the fule paths to the files checked by the user
+     * 
+     * @return      File paths to the files checked by the user.
+     */
+    public String[] getCheckedFilePaths() {
+        String [] result = null;
+        SparseBooleanArray positions = mList.getCheckedItemPositions();
+        if (positions.size() > 0) {
+            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();
+            }
+        }
+        return result;
+    }
+
+    
+    /**
+     * Interface to implement by any Activity that includes some instance of LocalFileListFragment
+     * 
+     * @author David A. Velasco
+     */
+    public interface ContainerActivity {
+
+        /**
+         * Callback method invoked when a directory is clicked by the user on the files list
+         *  
+         * @param file
+         */
+        public void onDirectoryClick(File directory);
+        
+        /**
+         * Callback method invoked when a file (non directory) is clicked by the user on the files list
+         *  
+         * @param file
+         */
+        public void onFileClick(File file);
+        
+        
+        /**
+         * Callback method invoked when the parent activity is fully created to get the directory to list firstly.
+         * 
+         * @return  Directory to list firstly. Can be NULL.
+         */
+        public File getInitialDirectory();
+
+    }
+
+
+}
diff --git a/src/com/owncloud/android/ui/fragment/OCFileListFragment.java b/src/com/owncloud/android/ui/fragment/OCFileListFragment.java
new file mode 100644 (file)
index 0000000..2c3f939
--- /dev/null
@@ -0,0 +1,478 @@
+/* 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.util.ArrayList;
+import java.util.List;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.FileHandler;
+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.RemoveFileOperation;
+import com.owncloud.android.operations.RenameFileOperation;
+import com.owncloud.android.operations.SynchronizeFileOperation;
+import com.owncloud.android.ui.activity.FileDisplayActivity;
+import com.owncloud.android.ui.activity.TransferServiceGetter;
+import com.owncloud.android.ui.adapter.FileListListAdapter;
+import com.owncloud.android.ui.dialog.EditNameDialog;
+import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
+import com.owncloud.android.ui.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
+import com.owncloud.android.ui.preview.PreviewImageFragment;
+import com.owncloud.android.ui.preview.PreviewMediaFragment;
+
+
+import android.accounts.Account;
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.ContextMenu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+
+/**
+ * A Fragment that lists all files and folders in a given path.
+ * 
+ * @author Bartek Przybylski
+ * 
+ */
+public class OCFileListFragment extends ExtendedListFragment implements EditNameDialogListener, ConfirmationDialogFragmentListener {
+    
+    private static final String TAG = OCFileListFragment.class.getSimpleName();
+
+    private static final String MY_PACKAGE = OCFileListFragment.class.getPackage() != null ? OCFileListFragment.class.getPackage().getName() : "com.owncloud.android.ui.fragment";
+    private static final String EXTRA_FILE = MY_PACKAGE + ".extra.FILE";
+    
+    private OCFileListFragment.ContainerActivity mContainerActivity;
+    
+    private OCFile mFile = null;
+    private FileListListAdapter mAdapter;
+    
+    private Handler mHandler;
+    private OCFile mTargetFile;
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        Log_OC.e(TAG, "onAttach");
+        try {
+            mContainerActivity = (ContainerActivity) activity;
+        } catch (ClassCastException e) {
+            throw new ClassCastException(activity.toString() + " must implement " + OCFileListFragment.ContainerActivity.class.getSimpleName());
+        }
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        Log_OC.e(TAG, "onActivityCreated() start");
+        mAdapter = new FileListListAdapter(getActivity(), mContainerActivity);
+        if (savedInstanceState != null) {
+            mFile = savedInstanceState.getParcelable(EXTRA_FILE);
+        }
+        setListAdapter(mAdapter);
+        
+        registerForContextMenu(getListView());
+        getListView().setOnCreateContextMenuListener(this);        
+        
+        mHandler = new Handler();
+
+    }
+    
+    /**
+     * Saves the current listed folder.
+     */
+    @Override
+    public void onSaveInstanceState (Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putParcelable(EXTRA_FILE, mFile);
+    }
+
+
+    /**
+     * Call this, when the user presses the up button
+     */
+    public void onBrowseUp() {
+        OCFile parentDir = null;
+        
+        if(mFile != null){
+            DataStorageManager storageManager = mContainerActivity.getStorageManager();
+            parentDir = storageManager.getFileById(mFile.getParentId());
+            mFile = parentDir;
+        }
+        listDirectory(parentDir);
+    }
+    
+    @Override
+    public void onItemClick(AdapterView<?> l, View v, int position, long id) {
+        OCFile file = (OCFile) mAdapter.getItem(position);
+        if (file != null) {
+            if (file.isDirectory()) { 
+                // update state and view of this fragment
+                listDirectory(file);
+                // then, notify parent activity to let it update its state and view, and other fragments
+                mContainerActivity.onBrowsedDownTo(file);
+                
+            } else { /// Click on a file
+                if (PreviewImageFragment.canBePreviewed(file)) {
+                    // preview image - it handles the download, if needed
+                    mContainerActivity.startImagePreview(file);
+                    
+                } else if (file.isDown()) {
+                    if (PreviewMediaFragment.canBePreviewed(file)) {
+                        // media preview
+                        mContainerActivity.startMediaPreview(file, 0, true);
+                    } else {
+                        // open with
+                        mContainerActivity.openFile(file);
+                    }
+                    
+                } else {
+                    // automatic download, preview on finish
+                    mContainerActivity.startDownloadForPreview(file);
+                }
+                    
+            }
+            
+        } else {
+            Log_OC.d(TAG, "Null object in ListAdapter!!");
+        }
+        
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onCreateContextMenu (ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+        super.onCreateContextMenu(menu, v, menuInfo);
+        MenuInflater inflater = getActivity().getMenuInflater();
+        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>();    
+        List<Integer> toDisable = new ArrayList<Integer>();  
+        
+        MenuItem item = null;
+        if (targetFile.isDirectory()) {
+            // contextual menu for folders
+            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_sync_file);
+            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.action_rename_file);
+                toDisable.add(R.id.action_remove_file);
+                
+            }
+            
+        } else {
+            // contextual menu for regular files
+            
+            // new design: 'download' and 'open with' won't be available anymore in context menu
+            toHide.add(R.id.action_download_file);
+            toHide.add(R.id.action_open_file_with);
+            
+            if (targetFile.isDown()) {
+                toHide.add(R.id.action_cancel_download);
+                toHide.add(R.id.action_cancel_upload);
+                
+            } else {
+                toHide.add(R.id.action_sync_file);
+            }
+            if ( mContainerActivity.getFileDownloaderBinder().isDownloading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile)) {
+                toHide.add(R.id.action_cancel_upload);
+                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.action_cancel_download);
+                toDisable.add(R.id.action_rename_file);
+                toDisable.add(R.id.action_remove_file);
+                    
+            } else {
+                toHide.add(R.id.action_cancel_download);
+                toHide.add(R.id.action_cancel_upload);
+            }
+        }
+
+        for (int i : toHide) {
+            item = menu.findItem(i);
+            if (item != null) {
+                item.setVisible(false);
+                item.setEnabled(false);
+            }
+        }
+        
+        for (int i : toDisable) {
+            item = menu.findItem(i);
+            if (item != null) {
+                item.setEnabled(false);
+            }
+        }
+    }
+    
+    
+    /**
+     * {@inhericDoc}
+     */
+    @Override
+    public boolean onContextItemSelected (MenuItem item) {
+        AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();        
+        mTargetFile = (OCFile) mAdapter.getItem(info.position);
+        switch (item.getItemId()) {
+            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.action_remove_file: {
+                int messageStringId = R.string.confirmation_remove_alert;
+                int posBtnStringId = R.string.confirmation_remove_remote;
+                int neuBtnStringId = -1;
+                if (mTargetFile.isDirectory()) {
+                    messageStringId = R.string.confirmation_remove_folder_alert;
+                    posBtnStringId = R.string.confirmation_remove_remote_and_local;
+                    neuBtnStringId = R.string.confirmation_remove_folder_local;
+                } else if (mTargetFile.isDown()) {
+                    posBtnStringId = R.string.confirmation_remove_remote_and_local;
+                    neuBtnStringId = R.string.confirmation_remove_local;
+                }
+                ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
+                        messageStringId,
+                        new String[]{mTargetFile.getFileName()},
+                        posBtnStringId,
+                        neuBtnStringId,
+                        R.string.common_cancel);
+                confDialog.setOnConfirmationListener(this);
+                confDialog.show(getFragmentManager(), FileDetailFragment.FTAG_CONFIRMATION);
+                return true;
+            }
+            case R.id.action_sync_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());
+                ((FileDisplayActivity) getSherlockActivity()).showLoadingDialog();
+                return true;
+            }
+            case R.id.action_cancel_download: {
+                FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
+                Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity());
+                if (downloaderBinder != null && downloaderBinder.isDownloading(account, mTargetFile)) {
+                    downloaderBinder.cancel(account, mTargetFile);
+                    listDirectory();
+                    mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
+                }
+                return true;
+            }
+            case R.id.action_cancel_upload: {
+                FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
+                Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity());
+                if (uploaderBinder != null && uploaderBinder.isUploading(account, mTargetFile)) {
+                    uploaderBinder.cancel(account, mTargetFile);
+                    listDirectory();
+                    mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
+                }
+                return true;
+            }
+            case R.id.action_see_details: {
+                ((FileFragment.ContainerActivity)getActivity()).showDetails(mTargetFile);
+                return true;
+            }
+            default:
+                return super.onContextItemSelected(item); 
+        }
+    }
+    
+
+    /**
+     * Use this to query the {@link OCFile} that is currently
+     * being displayed by this fragment
+     * @return The currently viewed OCFile
+     */
+    public OCFile getCurrentFile(){
+        return mFile;
+    }
+    
+    /**
+     * Calls {@link OCFileListFragment#listDirectory(OCFile)} with a null parameter
+     */
+    public void listDirectory(){
+        listDirectory(null);
+    }
+    
+    /**
+     * Lists the given directory on the view. When the input parameter is null,
+     * it will either refresh the last known directory. list the root
+     * if there never was a directory.
+     * 
+     * @param directory File to be listed
+     */
+    public void listDirectory(OCFile directory) {
+        DataStorageManager storageManager = mContainerActivity.getStorageManager();
+        if (storageManager != null) {
+
+            // Check input parameters for null
+            if(directory == null){
+                if(mFile != null){
+                    directory = mFile;
+                } else {
+                    directory = storageManager.getFileByPath("/");
+                    if (directory == null) return; // no files, wait for sync
+                }
+            }
+        
+        
+            // If that's not a directory -> List its parent
+            if(!directory.isDirectory()){
+                Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
+                directory = storageManager.getFileById(directory.getParentId());
+            }
+
+            mAdapter.swapDirectory(directory, storageManager);
+            if (mFile == null || !mFile.equals(directory)) {
+                mList.setSelectionFromTop(0, 0);
+            }
+            mFile = directory;
+
+        }
+    }
+    
+    
+    
+    /**
+     * Interface to implement by any Activity that includes some instance of FileListFragment
+     * 
+     * @author David A. Velasco
+     */
+    public interface ContainerActivity extends TransferServiceGetter, OnRemoteOperationListener, FileHandler {
+
+        /**
+         * Callback method invoked when a the user browsed into a different folder through the list of files
+         *  
+         * @param file
+         */
+        public void onBrowsedDownTo(OCFile folder);
+        
+        public void startDownloadForPreview(OCFile file);
+
+        public void startMediaPreview(OCFile file, int i, boolean b);
+
+        public void startImagePreview(OCFile file);
+
+        /**
+         * Getter for the current DataStorageManager in the container activity
+         */
+        public DataStorageManager getStorageManager();
+        
+        
+        /**
+         * Callback method invoked when a the 'transfer state' of a file changes.
+         * 
+         * This happens when a download or upload is started or ended for a file.
+         * 
+         * This method is necessary by now to update the user interface of the double-pane layout in tablets
+         * because methods {@link FileDownloaderBinder#isDownloading(Account, OCFile)} and {@link FileUploaderBinder#isUploading(Account, OCFile)}
+         * won't provide the needed response before the method where this is called finishes. 
+         * 
+         * TODO Remove this when the transfer state of a file is kept in the database (other thing TODO)
+         * 
+         * @param file          OCFile which state changed.
+         * @param downloading   Flag signaling if the file is now downloading.
+         * @param uploading     Flag signaling if the file is now uploading.
+         */
+        public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading);
+        
+    }
+    
+    
+    @Override
+    public void onDismiss(EditNameDialog dialog) {
+        if (dialog.getResult()) {
+            String newFilename = dialog.getNewFilename();
+            Log_OC.d(TAG, "name edit dialog dismissed with new name " + newFilename);
+            RemoteOperation operation = new RenameFileOperation(mTargetFile, 
+                                                                AccountUtils.getCurrentOwnCloudAccount(getActivity()), 
+                                                                newFilename, 
+                                                                mContainerActivity.getStorageManager());
+            operation.execute(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
+            ((FileDisplayActivity) getActivity()).showLoadingDialog();
+        }
+    }
+
+    
+    @Override
+    public void onConfirmation(String callerTag) {
+        if (callerTag.equals(FileDetailFragment.FTAG_CONFIRMATION)) {
+            if (mContainerActivity.getStorageManager().getFileById(mTargetFile.getFileId()) != null) {
+                RemoteOperation operation = new RemoveFileOperation( mTargetFile, 
+                                                                    true, 
+                                                                    mContainerActivity.getStorageManager());
+                operation.execute(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
+                
+                ((FileDisplayActivity) getActivity()).showLoadingDialog();
+            }
+        }
+    }
+    
+    @Override
+    public void onNeutral(String callerTag) {
+        File f = null;
+        if (mTargetFile.isDirectory()) {
+            // TODO run in a secondary thread?
+            mContainerActivity.getStorageManager().removeDirectory(mTargetFile, false, true);
+            
+        } else if (mTargetFile.isDown() && (f = new File(mTargetFile.getStoragePath())).exists()) {
+            f.delete();
+            mTargetFile.setStoragePath(null);
+            mContainerActivity.getStorageManager().saveFile(mTargetFile);
+        }
+        listDirectory();
+        mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
+    }
+    
+    @Override
+    public void onCancel(String callerTag) {
+        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..306df1f
--- /dev/null
@@ -0,0 +1,383 @@
+/* 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 com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
+import com.owncloud.android.ui.fragment.FileFragment;
+
+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.ImageButton;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+
+
+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 FileFragment implements OnClickListener {
+
+    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 Account mAccount;
+    
+    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() {
+        super();
+        mAccount = 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) {
+        super(fileToDetail);
+        mAccount = ocAccount;
+        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) {
+                setFile((OCFile)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);
+        
+        ((ImageButton)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, getFile());
+        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, getFile())) {
+                    downloaderBinder.cancel(mAccount, getFile());
+                    getActivity().finish(); // :)
+                    /*
+                    leaveTransferProgress();
+                    if (mFile.isDown()) {
+                        setButtonsForDown();
+                    } else {
+                        setButtonsForRemote();
+                    }
+                    */
+                }
+                break;
+            }
+            default:
+                Log_OC.e(TAG, "Incorrect view clicked!");
+        }
+    }
+
+    
+    /**
+     * 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, getFile()))) {
+            setButtonsForTransferring();
+            
+        } else if (getFile().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, getFile());
+                mListening = true;
+                setButtonsForTransferring();
+            }
+        }
+    }
+    
+    
+    public void leaveTransferProgress() {
+        if (mProgressListener != null) {
+            if (mContainerActivity.getFileDownloaderBinder() != null) {
+                mContainerActivity.getFileDownloaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, getFile());
+                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..8f12ac7
--- /dev/null
@@ -0,0 +1,467 @@
+/* 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.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.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
+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.view.MenuItem;
+import com.actionbarsherlock.view.Window;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
+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.FileUploader;
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
+import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
+import com.owncloud.android.ui.activity.FileActivity;
+import com.owncloud.android.ui.activity.FileDisplayActivity;
+import com.owncloud.android.ui.dialog.LoadingDialog;
+import com.owncloud.android.ui.fragment.FileFragment;
+
+
+/**
+ *  Holds a swiping galley where image files contained in an ownCloud directory are shown
+ *  
+ *  @author David A. Velasco
+ */
+public class PreviewImageActivity extends FileActivity 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 static final String DIALOG_WAIT_TAG = "DIALOG_WAIT";
+    
+    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;
+    
+    private String mDownloadAddedMessage;
+    private String mDownloadFinishMessage;
+    
+    
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
+        setContentView(R.layout.preview_image_activity);
+        
+        ActionBar actionBar = getSupportActionBar();
+        actionBar.setDisplayHomeAsUpEnabled(true);
+        actionBar.hide();
+        
+        mFullScreen = true;
+        if (savedInstanceState != null) {
+            mRequestWaitingForBinder = savedInstanceState.getBoolean(KEY_WAITING_FOR_BINDER);
+        } else {
+            mRequestWaitingForBinder = false;
+        }
+        
+        FileDownloader downloader = new FileDownloader();
+        mDownloadAddedMessage = downloader.getDownloadAddedMessage();
+        mDownloadFinishMessage= downloader.getDownloadFinishMessage();
+    }
+
+    private void initViewPager() {
+        // get parent from path
+        String parentPath = getFile().getRemotePath().substring(0, getFile().getRemotePath().lastIndexOf(getFile().getFileName()));
+        OCFile parentFolder = mStorageManager.getFileByPath(parentPath);
+        //OCFile parentFolder = mStorageManager.getFileById(getFile().getParentId());
+        if (parentFolder == null) {
+            // should not be necessary
+            parentFolder = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR);
+        }
+        mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(), parentFolder, getAccount(), mStorageManager);
+        mViewPager = (ViewPager) findViewById(R.id.fragmentPager);
+        int position = mPreviewImagePagerAdapter.getFilePosition(getFile());
+        position = (position >= 0) ? position : 0;
+        mViewPager.setAdapter(mPreviewImagePagerAdapter); 
+        mViewPager.setOnPageChangeListener(this);
+        mViewPager.setCurrentItem(position);
+        if (position == 0 && !getFile().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(mDownloadFinishMessage);
+        filter.addAction(mDownloadAddedMessage);
+        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() {
+        finish();
+    }
+    
+    /**
+     * Show loading dialog 
+     */
+    public void showLoadingDialog() {
+        // Construct dialog
+        LoadingDialog loading = new LoadingDialog(getResources().getString(R.string.wait_a_moment));
+        FragmentManager fm = getSupportFragmentManager();
+        FragmentTransaction ft = fm.beginTransaction();
+        loading.show(ft, DIALOG_WAIT_TAG);
+        
+    }
+    
+    /**
+     * Dismiss loading dialog
+     */
+    public void dismissLoadingDialog(){
+        Fragment frag = getSupportFragmentManager().findFragmentByTag(DIALOG_WAIT_TAG);
+      if (frag != null) {
+          LoadingDialog loading = (LoadingDialog) frag;
+            loading.dismiss();
+        }
+    }
+    
+    /**
+     * {@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 showDetails(OCFile file) {
+        Intent showDetailsIntent = new Intent(this, FileDisplayActivity.class);
+        showDetailsIntent.setAction(FileDisplayActivity.ACTION_DETAILS);
+        showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, file);
+        showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
+        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(getAccount(), file)) {
+            Intent i = new Intent(this, FileDownloader.class);
+            i.putExtra(FileDownloader.EXTRA_ACCOUNT, getAccount());
+            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 (getAccount().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(mDownloadFinishMessage)) {
+                    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;
+    }
+
+    @Override
+    protected void onAccountSet(boolean stateWasRecovered) {
+        if (getAccount() != null) {
+            OCFile file = getFile();
+            /// Validate handled file  (first image to preview)
+            if (file == null) {
+                throw new IllegalStateException("Instanced with a NULL OCFile");
+            }
+            if (!file.isImage()) {
+                throw new IllegalArgumentException("Non-image file passed as argument");
+            }
+            mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver());            
+            
+            // Update file according to DB file, if it is possible
+            if (file.getFileId() > DataStorageManager.ROOT_PARENT_ID)            
+                file = mStorageManager.getFileById(file.getFileId());
+            
+            if (file != null) {
+                /// Refresh the activity according to the Account and OCFile set
+                setFile(file);  // reset after getting it fresh from mStorageManager
+                getSupportActionBar().setTitle(getFile().getFileName());
+                //if (!stateWasRecovered) {
+                    initViewPager();
+                //}
+
+            } else {
+                // handled file not in the current Account
+                finish();
+            }
+            
+        } else {
+            Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
+        }
+    }
+    
+    
+}
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..9ef4db4
--- /dev/null
@@ -0,0 +1,631 @@
+/* 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.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+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 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 FileFragment implements   OnRemoteOperationListener, 
+                                                                        ConfirmationDialogFragment.ConfirmationDialogFragmentListener {
+    public static final String EXTRA_FILE = "FILE";
+    public static final String EXTRA_ACCOUNT = "ACCOUNT";
+
+    private View mView;
+    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) {
+        super(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() {
+        super();
+        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) {
+                setFile((OCFile)savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_FILE));
+                mAccount = savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_ACCOUNT);
+            } else {
+                mIgnoreFirstSavedState = false;
+            }
+        }
+        if (getFile() == null) {
+            throw new IllegalStateException("Instanced with a NULL OCFile");
+        }
+        if (mAccount == null) {
+            throw new IllegalStateException("Instanced with a NULL ownCloud Account");
+        }
+        if (!getFile().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, getFile());
+        outState.putParcelable(PreviewImageFragment.EXTRA_ACCOUNT, mAccount);
+    }
+    
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        if (getFile() != null) {
+           BitmapLoader bl = new BitmapLoader(mImageView, mMessageView, mProgressWheel);
+           bl.execute(new String[]{getFile().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()).showDetails(getFile());        
+    }
+
+
+    @Override
+    public void onResume() {
+        super.onResume();
+    }
+
+
+    @Override
+    public void onPause() {
+        super.onPause();
+    }
+
+
+    @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() {
+        OCFile file = getFile();
+        String storagePath = file.getStoragePath();
+        String encodedStoragePath = WebdavUtils.encodePath(storagePath);
+        try {
+            Intent i = new Intent(Intent.ACTION_VIEW);
+            i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), file.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: " + file.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(file.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 " + file.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[]{getFile().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(getFile().getFileId()) != null) {   // check that the file is still there;
+            mLastRemoteOperation = new RemoveFileOperation( getFile(),      // 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());
+            
+            ((PreviewImageActivity) getActivity()).showLoadingDialog();
+        }
+    }
+    
+    
+    /**
+     * Removes the file from local storage
+     */
+    @Override
+    public void onNeutral(String callerTag) {
+        // TODO this code should be made in a secondary thread,
+        OCFile file = getFile();
+        if (file.isDown()) {   // checks it is still there
+            File f = new File(file.getStoragePath());
+            f.delete();
+            file.setStoragePath(null);
+            mStorageManager.saveFile(file);
+            finish();
+        }
+    }
+    
+    /**
+     * User cancelled the removal action.
+     */
+    @Override
+    public void onCancel(String callerTag) {
+        // nothing to do here
+    }
+    
+
+    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 " + getFile().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) {
+        ((PreviewImageActivity) getActivity()).dismissLoadingDialog();
+        
+        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..d17d35f
--- /dev/null
@@ -0,0 +1,333 @@
+/* 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 com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.ui.fragment.FileFragment;
+
+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;
+
+
+/**
+ * Adapter class that provides Fragment instances  
+ * 
+ * @author David A. Velasco
+ */
+//public class PreviewImagePagerAdapter extends PagerAdapter {
+public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter {
+    
+    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;
+
+    /**
+     * 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..815dbbd
--- /dev/null
@@ -0,0 +1,769 @@
+/* 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.content.res.Configuration;
+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.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.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+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.FileActivity;
+import com.owncloud.android.ui.activity.FileDisplayActivity;
+import com.owncloud.android.ui.fragment.ConfirmationDialogFragment;
+import com.owncloud.android.ui.fragment.FileFragment;
+
+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 FileFragment implements
+        OnTouchListener,  
+        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 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;
+    public boolean mPrepared;
+    
+    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, int startPlaybackPosition, boolean autoplay) {
+        super(fileToDetail);
+        mAccount = ocAccount;
+        mSavedPlaybackPosition = startPlaybackPosition;
+        mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment 
+        mAutoplay = autoplay;
+    }
+    
+    
+    /**
+     *  Creates an empty fragment for previews.
+     * 
+     *  MUST BE KEPT: the system uses it when tries to reinstantiate a fragment automatically (for instance, when the device is turned a aside).
+     * 
+     *  DO NOT CALL IT: an {@link OCFile} and {@link Account} must be provided for a successful construction 
+     */
+    public PreviewMediaFragment() {
+        super();
+        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);
+        Log_OC.e(TAG, "onCreateView");
+
+        
+        mView = inflater.inflate(R.layout.file_preview, container, false);
+        
+        mImagePreview = (ImageView)mView.findViewById(R.id.image_preview);
+        mVideoPreview = (VideoView)mView.findViewById(R.id.video_preview);
+        mVideoPreview.setOnTouchListener(this);
+        
+        mMediaController = (MediaControlView)mView.findViewById(R.id.media_controller);
+        
+        return mView;
+    }
+    
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        Log_OC.e(TAG, "onAttach");
+        
+        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);
+        Log_OC.e(TAG, "onActivityCreated");
+
+        mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());
+        if (savedInstanceState != null) {
+            setFile((OCFile)savedInstanceState.getParcelable(PreviewMediaFragment.EXTRA_FILE));
+            mAccount = savedInstanceState.getParcelable(PreviewMediaFragment.EXTRA_ACCOUNT);
+            mSavedPlaybackPosition = savedInstanceState.getInt(PreviewMediaFragment.EXTRA_PLAY_POSITION);
+            mAutoplay = savedInstanceState.getBoolean(PreviewMediaFragment.EXTRA_PLAYING);
+            
+        }
+        OCFile file = getFile();
+        if (file == null) {
+            throw new IllegalStateException("Instanced with a NULL OCFile");
+        }
+        if (mAccount == null) {
+            throw new IllegalStateException("Instanced with a NULL ownCloud Account");
+        }
+        if (!file.isDown()) {
+            throw new IllegalStateException("There is no local file to preview");
+        }
+        if (file.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);
+        Log_OC.e(TAG, "onSaveInstanceState");
+        
+        outState.putParcelable(PreviewMediaFragment.EXTRA_FILE, getFile());
+        outState.putParcelable(PreviewMediaFragment.EXTRA_ACCOUNT, mAccount);
+        
+        if (getFile().isVideo()) {
+            mSavedPlaybackPosition = mVideoPreview.getCurrentPosition();
+            mAutoplay = mVideoPreview.isPlaying();
+            outState.putInt(PreviewMediaFragment.EXTRA_PLAY_POSITION , mSavedPlaybackPosition);
+            outState.putBoolean(PreviewMediaFragment.EXTRA_PLAYING , mAutoplay);
+        } else {
+            outState.putInt(PreviewMediaFragment.EXTRA_PLAY_POSITION , mMediaServiceBinder.getCurrentPosition());
+            outState.putBoolean(PreviewMediaFragment.EXTRA_PLAYING , mMediaServiceBinder.isPlaying());
+        }
+    }
+    
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        Log_OC.e(TAG, "onStart");
+
+        OCFile file = getFile();
+        if (file != null) {
+           if (file.isAudio()) {
+               bindMediaService();
+               
+           } else if (file.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_sync_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()).showDetails(getFile());        
+    }
+
+
+    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(getFile().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();
+            mPrepared = true;
+        }
+        
+        
+        /**
+         * 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(getFile().getStoragePath());
+                }
+            } // else : called from onError()
+            mMediaController.updatePausePlay();
+        }
+        
+        
+        /**
+         * Called when an error in playback occurs.
+         * 
+         * @param   mp      {@link MediaPlayer} instance performing the playback.
+         * @param   what    Type of error
+         * @param   extra   Extra code specific to the error
+         */
+        @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 onPause() {
+        super.onPause();
+        Log_OC.e(TAG, "onPause");
+    }
+    
+    @Override
+    public void onResume() {
+        super.onResume();
+        Log_OC.e(TAG, "onResume");
+    }
+    
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        Log_OC.e(TAG, "onDestroy");
+    }
+    
+    @Override
+    public void onStop() {
+        Log_OC.e(TAG, "onStop");
+        super.onStop();
+
+        mPrepared = false;
+        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(FileActivity.EXTRA_ACCOUNT, mAccount);
+        i.putExtra(FileActivity.EXTRA_FILE, getFile());
+        i.putExtra(PreviewVideoActivity.EXTRA_AUTOPLAY, mVideoPreview.isPlaying());
+        mVideoPreview.pause();
+        i.putExtra(PreviewVideoActivity.EXTRA_START_POSITION, mVideoPreview.getCurrentPosition());
+        startActivityForResult(i, 0);
+    }
+
+    @Override
+    public void onConfigurationChanged (Configuration newConfig) {
+        Log_OC.e(TAG, "onConfigurationChanged " + this);
+    }
+    
+    @Override
+    public void onActivityResult (int requestCode, int resultCode, Intent data) {
+        Log_OC.e(TAG, "onActivityResult " + this);
+        super.onActivityResult(requestCode, resultCode, data);
+        if (resultCode == Activity.RESULT_OK) {
+            mSavedPlaybackPosition = data.getExtras().getInt(PreviewVideoActivity.EXTRA_START_POSITION);
+            mAutoplay = data.getExtras().getBoolean(PreviewVideoActivity.EXTRA_AUTOPLAY); 
+        }
+    }
+    
+
+    private void playAudio() {
+        OCFile file = getFile();
+        if (!mMediaServiceBinder.isPlaying(file)) {
+            Log_OC.d(TAG, "starting playback of " + file.getStoragePath());
+            mMediaServiceBinder.start(mAccount, file, mAutoplay, mSavedPlaybackPosition);
+            
+        } else {
+            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() {
+        OCFile file = getFile();
+        stopPreview(true);
+        String storagePath = file.getStoragePath();
+        String encodedStoragePath = WebdavUtils.encodePath(storagePath);
+        try {
+            Intent i = new Intent(Intent.ACTION_VIEW);
+            i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), file.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: " + file.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(file.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 " + file.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[]{getFile().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) {
+        OCFile file = getFile();
+        if (mStorageManager.getFileById(file.getFileId()) != null) {   // check that the file is still there;
+            stopPreview(true);
+            mLastRemoteOperation = new RemoveFileOperation( file,      // 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());
+            
+            ((FileDisplayActivity) getActivity()).showLoadingDialog();
+        }
+    }
+    
+    
+    /**
+     * Removes the file from local storage
+     */
+    @Override
+    public void onNeutral(String callerTag) {
+        // TODO this code should be made in a secondary thread,
+        OCFile file = getFile();
+        if (file.isDown()) {   // checks it is still there
+            stopPreview(true);
+            File f = new File(file.getStoragePath());
+            f.delete();
+            file.setStoragePath(null);
+            mStorageManager.saveFile(file);
+            finish();
+        }
+    }
+    
+    /**
+     * User cancelled the removal action.
+     */
+    @Override
+    public void onCancel(String callerTag) {
+        // nothing to do here
+    }
+    
+
+    /**
+     * 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) {
+        ((FileDisplayActivity) getActivity()).dismissLoadingDialog();
+        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) {
+        OCFile file = getFile();
+        if (file.isAudio() && stopAudio) {
+            mMediaServiceBinder.pause();
+            
+        } else if (file.isVideo()) {
+            mVideoPreview.stopPlayback();
+        }
+    }
+
+
+
+    /**
+     * Finishes the preview
+     */
+    private void finish() {
+        getActivity().onBackPressed();
+    }
+
+
+    public int getPosition() {
+        if (mPrepared) {
+            mSavedPlaybackPosition = mVideoPreview.getCurrentPosition();
+        }
+        Log_OC.e(TAG, "getting position: " + mSavedPlaybackPosition);
+        return mSavedPlaybackPosition;
+    }
+    
+    public boolean isPlaying() {
+        if (mPrepared) {
+            mAutoplay = mVideoPreview.isPlaying();
+        }
+        return mAutoplay;
+    }
+    
+}
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..56972c6
--- /dev/null
@@ -0,0 +1,240 @@
+/* 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 com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.authentication.AccountUtils.AccountNotFoundException;
+import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.media.MediaService;
+import com.owncloud.android.ui.activity.FileActivity;
+
+import android.accounts.Account;
+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.widget.MediaController;
+import android.widget.VideoView;
+
+
+/**
+ *  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 FileActivity implements OnCompletionListener, OnPreparedListener, OnErrorListener {
+
+    /** 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 DataStorageManager mStorageManager;
+    
+    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();
+            mSavedPlaybackPosition = extras.getInt(EXTRA_START_POSITION);
+            mAutoplay = extras.getBoolean(EXTRA_AUTOPLAY);
+            
+        } else {
+            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);
+    }    
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        Log_OC.e(TAG, "ACTIVITY\t\tonSaveInstanceState");
+        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();
+    }
+
+    
+    /** 
+     * 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;
+    }
+    
+    
+    @Override
+    protected void onAccountSet(boolean stateWasRecovered) {
+        if (getAccount() != null) {
+            OCFile file = getFile();
+            /// Validate handled file  (first image to preview)
+            if (file == null) {
+                throw new IllegalStateException("Instanced with a NULL OCFile");
+            }
+            if (!file.isVideo()) {
+                throw new IllegalArgumentException("Non-video file passed as argument");
+            }
+            mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver());
+            file = mStorageManager.getFileById(file.getFileId()); 
+            if (file != null) {
+                if (file.isDown()) {
+                    mVideoPlayer.setVideoPath(file.getStoragePath());
+                    
+                } else {
+                    // not working yet
+                    String url;
+                    try {
+                        url = AccountUtils.constructFullURLForAccount(this, getAccount()) + file.getRemotePath();
+                        mVideoPlayer.setVideoURI(Uri.parse(url));
+                    } catch (AccountNotFoundException e) {
+                        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 {
+                finish();
+            }
+        } else {
+            Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
+            finish();
+        }
+   }
+
+
+}
\ No newline at end of file
diff --git a/src/com/owncloud/android/utils/FileStorageUtils.java b/src/com/owncloud/android/utils/FileStorageUtils.java
new file mode 100644 (file)
index 0000000..0c7e991
--- /dev/null
@@ -0,0 +1,80 @@
+/* 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.utils;
+
+import java.io.File;
+
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
+import com.owncloud.android.datamodel.OCFile;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.StatFs;
+
+
+/**
+ * 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() + "/" + MainApp.getDataFolder() + "/" + 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() + "/" + MainApp.getDataFolder() + "/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 + MainApp.getDataFolder() + 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
diff --git a/src/com/owncloud/android/utils/OwnCloudVersion.java b/src/com/owncloud/android/utils/OwnCloudVersion.java
new file mode 100644 (file)
index 0000000..630e7ac
--- /dev/null
@@ -0,0 +1,85 @@
+/* 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.utils;
+
+public class OwnCloudVersion implements Comparable<OwnCloudVersion> {
+    public static final OwnCloudVersion owncloud_v1 = new OwnCloudVersion(
+            0x010000);
+    public static final OwnCloudVersion owncloud_v2 = new OwnCloudVersion(
+            0x020000);
+    public static final OwnCloudVersion owncloud_v3 = new OwnCloudVersion(
+            0x030000);
+    public static final OwnCloudVersion owncloud_v4 = new OwnCloudVersion(
+            0x040000);
+    public static final OwnCloudVersion owncloud_v4_5 = new OwnCloudVersion(
+            0x040500);
+
+    // format is in version
+    // 0xAABBCC
+    // for version AA.BB.CC
+    // ie version 2.0.3 will be stored as 0x030003
+    private int mVersion;
+    private boolean mIsValid;
+
+    public OwnCloudVersion(int version) {
+        mVersion = version;
+        mIsValid = true;
+    }
+
+    public OwnCloudVersion(String version) {
+        mVersion = 0;
+        mIsValid = false;
+        parseVersionString(version);
+    }
+
+    public String toString() {
+        return ((mVersion >> 16) % 256) + "." + ((mVersion >> 8) % 256) + "."
+                + ((mVersion) % 256);
+    }
+
+    public boolean isVersionValid() {
+        return mIsValid;
+    }
+
+    @Override
+    public int compareTo(OwnCloudVersion another) {
+        return another.mVersion == mVersion ? 0
+                : another.mVersion < mVersion ? 1 : -1;
+    }
+
+    private void parseVersionString(String version) {
+        try {
+            String[] nums = version.split("\\.");
+            if (nums.length > 0) {
+                mVersion += Integer.parseInt(nums[0]);
+            }
+            mVersion = mVersion << 8;
+            if (nums.length > 1) {
+                mVersion += Integer.parseInt(nums[1]);
+            }
+            mVersion = mVersion << 8;
+            if (nums.length > 2) {
+                mVersion += Integer.parseInt(nums[2]);
+            }
+            mIsValid = true;
+        } catch (Exception e) {
+            mIsValid = false;
+        }
+    }
+}
diff --git a/src/com/owncloud/android/utils/RecursiveFileObserver.java b/src/com/owncloud/android/utils/RecursiveFileObserver.java
new file mode 100644 (file)
index 0000000..be44f8f
--- /dev/null
@@ -0,0 +1,101 @@
+/* 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.utils;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Stack;
+
+import android.os.FileObserver;
+
+public class RecursiveFileObserver extends FileObserver {
+
+    public static int CHANGES_ONLY = CLOSE_WRITE | MOVE_SELF | MOVED_FROM;
+    
+    List<SingleFileObserver> mObservers;
+    String mPath;
+    int mMask;
+    
+    public RecursiveFileObserver(String path) {
+        this(path, ALL_EVENTS);
+    }
+    
+    public RecursiveFileObserver(String path, int mask) {
+        super(path, mask);
+        mPath = path;
+        mMask = mask;
+    }
+
+    @Override
+    public void startWatching() {
+        if (mObservers != null) return;
+        mObservers = new ArrayList<SingleFileObserver>();
+        Stack<String> stack = new Stack<String>();
+        stack.push(mPath);
+        
+        while (!stack.empty()) {
+            String parent = stack.pop();
+            mObservers.add(new SingleFileObserver(parent, mMask));
+            File path = new File(parent);
+            File[] files = path.listFiles();
+            if (files == null) continue;
+            for (int i = 0; i < files.length; ++i) {
+                if (files[i].isDirectory() && !files[i].getName().equals(".")
+                    && !files[i].getName().equals("..")) {
+                    stack.push(files[i].getPath());
+                }
+            }
+        }
+        for (int i = 0; i < mObservers.size(); i++)
+            mObservers.get(i).startWatching();
+    }
+    
+    @Override
+    public void stopWatching() {
+        if (mObservers == null) return;
+        
+        for (int i = 0; i < mObservers.size(); ++i)
+            mObservers.get(i).stopWatching();
+
+        mObservers.clear();
+        mObservers = null;
+    }
+    
+    @Override
+    public void onEvent(int event, String path) {
+        
+    }
+    
+    private class SingleFileObserver extends FileObserver {
+        private String mPath;
+
+        public SingleFileObserver(String path, int mask) {
+            super(path, mask);
+            mPath = path;
+        }
+        
+        @Override
+        public void onEvent(int event, String path) {
+            String newPath = mPath + "/" + path;
+            RecursiveFileObserver.this.onEvent(event, newPath);
+        } 
+        
+    }
+}
diff --git a/src/com/owncloud/android/widgets/ActionEditText.java b/src/com/owncloud/android/widgets/ActionEditText.java
new file mode 100644 (file)
index 0000000..1077d15
--- /dev/null
@@ -0,0 +1,146 @@
+/* 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.widgets;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import com.owncloud.android.R;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.EditText;
+
+public class ActionEditText extends EditText {
+    private String s;
+    private String optionOneString;
+    private int optionOneColor;
+    private String optionTwoString;
+    private int optionTwoColor;
+    private Rect mTextBounds, mButtonRect;
+
+    private String badgeClickCallback;
+    private Rect btn_rect;
+
+    public ActionEditText(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        getAttrs(attrs);
+        s = optionOneString;
+        mTextBounds = new Rect();
+        mButtonRect = new Rect();
+    }
+
+    public ActionEditText(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        getAttrs(attrs);
+        s = optionOneString;
+        mTextBounds = new Rect();
+        mButtonRect = new Rect();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+        Paint p = getPaint();
+
+        p.getTextBounds(s, 0, s.length(), mTextBounds);
+
+        getDrawingRect(mButtonRect);
+        mButtonRect.top += 10;
+        mButtonRect.bottom -= 10;
+        mButtonRect.left = (int) (getWidth() - mTextBounds.width() - 18);
+        mButtonRect.right = getWidth() - 10;
+        btn_rect = mButtonRect;
+
+        if (s.equals(optionOneString))
+            p.setColor(optionOneColor);
+        else
+            p.setColor(optionTwoColor);
+        canvas.drawRect(mButtonRect, p);
+        p.setColor(Color.GRAY);
+
+        canvas.drawText(s, mButtonRect.left + 3, mButtonRect.bottom
+                - (mTextBounds.height() / 2), p);
+
+        invalidate();
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        int touchX = (int) event.getX();
+        int touchY = (int) event.getY();
+        boolean r = super.onTouchEvent(event);
+        if (event.getAction() == MotionEvent.ACTION_UP) {
+            if (btn_rect.contains(touchX, touchY)) {
+                if (s.equals(optionTwoString))
+                    s = optionOneString;
+                else
+                    s = optionTwoString;
+                if (badgeClickCallback != null) {
+                    @SuppressWarnings("rawtypes")
+                    Class[] paramtypes = new Class[2];
+                    paramtypes[0] = android.view.View.class;
+                    paramtypes[1] = String.class;
+                    Method method;
+                    try {
+
+                        method = getContext().getClass().getMethod(
+                                badgeClickCallback, paramtypes);
+                        method.invoke(getContext(), this, s);
+
+                    } catch (NoSuchMethodException e) {
+                        e.printStackTrace();
+                    } catch (IllegalArgumentException e) {
+                        e.printStackTrace();
+                    } catch (IllegalAccessException e) {
+                        e.printStackTrace();
+                    } catch (InvocationTargetException e) {
+                        e.printStackTrace();
+                    }
+
+                    invalidate();
+                }
+            }
+        }
+        return r;
+    }
+
+    private void getAttrs(AttributeSet attr) {
+        TypedArray a = getContext().obtainStyledAttributes(attr,
+                R.styleable.ActionEditText);
+        optionOneString = a
+                .getString(R.styleable.ActionEditText_optionOneString);
+        optionTwoString = a
+                .getString(R.styleable.ActionEditText_optionTwoString);
+        optionOneColor = a.getColor(R.styleable.ActionEditText_optionOneColor,
+                0x00ff00);
+        optionTwoColor = a.getColor(R.styleable.ActionEditText_optionTwoColor,
+                0xff0000);
+        badgeClickCallback = a
+                .getString(R.styleable.ActionEditText_onBadgeClick);
+    }
+
+}
diff --git a/src/de/mobilcom/debitel/cloud/android/DisplayUtils.java b/src/de/mobilcom/debitel/cloud/android/DisplayUtils.java
deleted file mode 100644 (file)
index 4b17755..0000000
+++ /dev/null
@@ -1,190 +0,0 @@
-/* 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 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 de.mobilcom.debitel.cloud.android;\r
-\r
-import java.util.Arrays;\r
-import java.util.Date;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.Set;\r
-\r
-/**\r
- * A helper class for some string operations.\r
- * \r
- * @author Bartek Przybylski\r
- * @author David A. Velasco\r
- */\r
-public class DisplayUtils {\r
-    \r
-    //private static String TAG = DisplayUtils.class.getSimpleName(); \r
-    \r
-    private static final String[] sizeSuffixes = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };\r
-\r
-    private static HashMap<String, String> mimeType2HUmanReadable;\r
-    static {\r
-        mimeType2HUmanReadable = new HashMap<String, String>();\r
-        // images\r
-        mimeType2HUmanReadable.put("image/jpeg", "JPEG image");\r
-        mimeType2HUmanReadable.put("image/jpg", "JPEG image");\r
-        mimeType2HUmanReadable.put("image/png", "PNG image");\r
-        mimeType2HUmanReadable.put("image/bmp", "Bitmap image");\r
-        mimeType2HUmanReadable.put("image/gif", "GIF image");\r
-        mimeType2HUmanReadable.put("image/svg+xml", "JPEG image");\r
-        mimeType2HUmanReadable.put("image/tiff", "TIFF image");\r
-        // music\r
-        mimeType2HUmanReadable.put("audio/mpeg", "MP3 music file");\r
-        mimeType2HUmanReadable.put("application/ogg", "OGG music file");\r
-\r
-    }\r
-\r
-    private static final String TYPE_APPLICATION = "application";\r
-    private static final String TYPE_AUDIO = "audio";\r
-    private static final String TYPE_IMAGE = "image";\r
-    private static final String TYPE_TXT = "text";\r
-    private static final String TYPE_VIDEO = "video";\r
-    \r
-    private static final String SUBTYPE_PDF = "pdf";\r
-    private static final String[] SUBTYPES_DOCUMENT = { "msword", "mspowerpoint", "msexcel", \r
-                                                        "vnd.oasis.opendocument.presentation",\r
-                                                        "vnd.oasis.opendocument.spreadsheet",\r
-                                                        "vnd.oasis.opendocument.text"\r
-                                                        };\r
-    private static Set<String> SUBTYPES_DOCUMENT_SET = new HashSet<String>(Arrays.asList(SUBTYPES_DOCUMENT));\r
-    private static final String[] SUBTYPES_COMPRESSED = {"x-tar", "x-gzip", "zip"};\r
-    private static final Set<String> SUBTYPES_COMPRESSED_SET = new HashSet<String>(Arrays.asList(SUBTYPES_COMPRESSED));\r
-    \r
-    /**\r
-     * Converts the file size in bytes to human readable output.\r
-     * \r
-     * @param bytes Input file size\r
-     * @return Like something readable like "12 MB"\r
-     */\r
-    public static String bytesToHumanReadable(long bytes) {\r
-        double result = bytes;\r
-        int attachedsuff = 0;\r
-        while (result > 1024 && attachedsuff < sizeSuffixes.length) {\r
-            result /= 1024.;\r
-            attachedsuff++;\r
-        }\r
-        result = ((int) (result * 100)) / 100.;\r
-        return result + " " + sizeSuffixes[attachedsuff];\r
-    }\r
-\r
-    /**\r
-     * Removes special HTML entities from a string\r
-     * \r
-     * @param s Input string\r
-     * @return A cleaned version of the string\r
-     */\r
-    public static String HtmlDecode(String s) {\r
-        /*\r
-         * TODO: Perhaps we should use something more proven like:\r
-         * http://commons.apache.org/lang/api-2.6/org/apache/commons/lang/StringEscapeUtils.html#unescapeHtml%28java.lang.String%29\r
-         */\r
-\r
-        String ret = "";\r
-        for (int i = 0; i < s.length(); ++i) {\r
-            if (s.charAt(i) == '%') {\r
-                ret += (char) Integer.parseInt(s.substring(i + 1, i + 3), 16);\r
-                i += 2;\r
-            } else {\r
-                ret += s.charAt(i);\r
-            }\r
-        }\r
-        return ret;\r
-    }\r
-\r
-    /**\r
-     * Converts MIME types like "image/jpg" to more end user friendly output\r
-     * like "JPG image".\r
-     * \r
-     * @param mimetype MIME type to convert\r
-     * @return A human friendly version of the MIME type\r
-     */\r
-    public static String convertMIMEtoPrettyPrint(String mimetype) {\r
-        if (mimeType2HUmanReadable.containsKey(mimetype)) {\r
-            return mimeType2HUmanReadable.get(mimetype);\r
-        }\r
-        if (mimetype.split("/").length >= 2)\r
-            return mimetype.split("/")[1].toUpperCase() + " file";\r
-        return "Unknown type";\r
-    }\r
-    \r
-    \r
-    /**\r
-     * Returns the resource identifier of an image resource to use as icon associated to a \r
-     * known MIME type.\r
-     * \r
-     * @param mimetype      MIME type string.\r
-     * @return              Resource identifier of an image resource.\r
-     */\r
-    public static int getResourceId(String mimetype) {\r
-\r
-        if (mimetype == null || "DIR".equals(mimetype)) {\r
-            return R.drawable.ic_menu_archive;\r
-            \r
-        } else {\r
-            String [] parts = mimetype.split("/");\r
-            String type = parts[0];\r
-            String subtype = (parts.length > 1) ? parts[1] : "";\r
-            \r
-            if(TYPE_TXT.equals(type)) {\r
-                return R.drawable.file_doc;\r
-    \r
-            } else if(TYPE_IMAGE.equals(type)) {\r
-                return R.drawable.file_image;\r
-                \r
-            } else if(TYPE_VIDEO.equals(type)) {\r
-                return R.drawable.file_movie;\r
-                \r
-            } else if(TYPE_AUDIO.equals(type)) {  \r
-                return R.drawable.file_sound;\r
-                \r
-            } else if(TYPE_APPLICATION.equals(type)) {\r
-                \r
-                if (SUBTYPE_PDF.equals(subtype)) {\r
-                    return R.drawable.file_pdf;\r
-                    \r
-                } else if (SUBTYPES_DOCUMENT_SET.contains(subtype)) {\r
-                    return R.drawable.file_doc;\r
-\r
-                } else if (SUBTYPES_COMPRESSED_SET.contains(subtype)) {\r
-                    return R.drawable.file_zip;\r
-                }\r
-    \r
-            }\r
-            // problems: RAR, RTF, 3GP are send as application/octet-stream from the server ; extension in the filename should be explicitly reviewed\r
-        }\r
-\r
-        // default icon\r
-        return R.drawable.file;\r
-    }\r
-\r
-    \r
-\r
-    /**\r
-     * Converts Unix time to human readable format\r
-     * @param miliseconds that have passed since 01/01/1970\r
-     * @return The human readable time for the users locale\r
-     */\r
-    public static String unixTimeToHumanReadable(long milliseconds) {\r
-        Date date = new Date(milliseconds);\r
-        return date.toLocaleString();\r
-    }\r
-}\r
diff --git a/src/de/mobilcom/debitel/cloud/android/Log_OC.java b/src/de/mobilcom/debitel/cloud/android/Log_OC.java
deleted file mode 100644 (file)
index 48d4277..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-package de.mobilcom.debitel.cloud.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 + MainApp.getLogName() + 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(); 
-        } 
-    }
-}
-
-    
-   
-
-  
-
-   
-   
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/MainApp.java b/src/de/mobilcom/debitel/cloud/android/MainApp.java
deleted file mode 100644 (file)
index 21bf305..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android;
-
-import android.app.Application;
-import android.content.Context;
-/**
- * Main Application of the project
- * 
- * Contains methods to build the "static" strings. These strings were before constants in different classes
- * 
- * @author masensio
- */
-public class MainApp extends Application {
-
-    private static Context mContext;
-
-    public void onCreate(){
-        super.onCreate();
-        MainApp.mContext = getApplicationContext();
-    }
-
-    public static Context getAppContext() {
-        return MainApp.mContext;
-    }
-
-    // Methods to obtain Strings referring app_name 
-    //   From AccountAuthenticator 
-    //   public static final String ACCOUNT_TYPE = "owncloud";    
-    public static String getAccountType() {
-        return getAppContext().getResources().getString(R.string.account_type);
-    }
-    
-    //  From AccountAuthenticator 
-    //  public static final String AUTHORITY = "org.owncloud";
-    public static String getAuthority() {
-        return getAppContext().getResources().getString(R.string.authority);
-    }
-    
-    //  From AccountAuthenticator
-    //  public static final String AUTH_TOKEN_TYPE = "org.owncloud";
-    public static String getAuthTokenType() {
-        return getAppContext().getResources().getString(R.string.authority);
-    }
-    
-    //  From AccountAuthenticator
-    //  public static final String AUTH_TOKEN_TYPE_PASSWORD = "owncloud.password";
-    public static String getAuthTokenTypePass() {
-        return getAppContext().getResources().getString(R.string.account_type) + ".password";
-    }
-    
-    //  From AccountAuthenticator
-    //  public static final String AUTH_TOKEN_TYPE_ACCESS_TOKEN = "owncloud.oauth2.access_token";
-    public static String getAuthTokenTypeAccessToken() {
-        return getAppContext().getResources().getString(R.string.account_type) + ".oauth2.access_token";
-    }
-    
-    //  From AccountAuthenticator
-    //  public static final String AUTH_TOKEN_TYPE_REFRESH_TOKEN = "owncloud.oauth2.refresh_token";
-    public static String getAuthTokenTypeRefreshToken() {
-        return getAppContext().getResources().getString(R.string.account_type) + ".oauth2.refresh_token";
-    }
-    
-    //  From AccountAuthenticator
-    //  public static final String AUTH_TOKEN_TYPE_SAML_WEB_SSO_SESSION_COOKIE = "owncloud.saml.web_sso.session_cookie";
-    public static String getAuthTokenTypeSamlSessionCookie() {
-        return getAppContext().getResources().getString(R.string.account_type) +  ".saml.web_sso.session_cookie";
-    }
-    
-    //  From ProviderMeta 
-    //  public static final String DB_FILE = "owncloud.db";
-    public static String getDBFile() {
-        return getAppContext().getResources().getString(R.string.db_file);
-    }
-    
-    //  From ProviderMeta
-    //  private final String mDatabaseName = "ownCloud";
-    public static String getDBName() {
-        return getAppContext().getResources().getString(R.string.db_name);
-    }
-     
-    //  data_folder
-    public static String getDataFolder() {
-        return getAppContext().getResources().getString(R.string.data_folder);
-    }
-    
-    // log_name
-    public static String getLogName() {
-        return getAppContext().getResources().getString(R.string.log_name);
-    }
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/OwnCloudSession.java b/src/de/mobilcom/debitel/cloud/android/OwnCloudSession.java
deleted file mode 100644 (file)
index 4e81578..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/* 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 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
-package de.mobilcom.debitel.cloud.android;\r
-\r
-/**\r
- * Represents a session to an ownCloud instance\r
- * \r
- * @author Bartek Przybylski\r
- * \r
- */\r
-public class OwnCloudSession {\r
-    private String mSessionName;\r
-    private String mSessionUrl;\r
-    private int mEntryId;\r
-\r
-    public OwnCloudSession(String name, String url, int entryId) {\r
-        mSessionName = name;\r
-        mSessionUrl = url;\r
-        mEntryId = entryId;\r
-    }\r
-\r
-    public void setName(String name) {\r
-        mSessionName = name;\r
-    }\r
-\r
-    public String getName() {\r
-        return mSessionName;\r
-    }\r
-\r
-    public void setUrl(String url) {\r
-        mSessionUrl = url;\r
-    }\r
-\r
-    public String getUrl() {\r
-        return mSessionUrl;\r
-    }\r
-\r
-    public int getEntryId() {\r
-        return mEntryId;\r
-    }\r
-}\r
diff --git a/src/de/mobilcom/debitel/cloud/android/Uploader.java b/src/de/mobilcom/debitel/cloud/android/Uploader.java
deleted file mode 100644 (file)
index d788136..0000000
+++ /dev/null
@@ -1,428 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.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 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.Audio;
-import android.provider.MediaStore.Images;
-import android.provider.MediaStore.Video;
-import android.view.View;
-import android.view.Window;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.EditText;
-import android.widget.SimpleAdapter;
-import android.widget.Toast;
-
-
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.authentication.AccountAuthenticator;
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader;
-import de.mobilcom.debitel.cloud.android.ui.CustomButton;
-
-/**
- * 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 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 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 (prepareStreamsToUpload()) {
-            mAccountManager = (AccountManager) getSystemService(Context.ACCOUNT_SERVICE);
-            Account[] accounts = mAccountManager.getAccountsByType(MainApp.getAccountType());
-            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[] { MainApp.getAuthTokenType() });
-                        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_MULTIPLE_ACCOUNT:
-            CharSequence ac[] = new CharSequence[mAccountManager.getAccountsByType(MainApp.getAccountType()).length];
-            for (int i = 0; i < ac.length; ++i) {
-                ac[i] = mAccountManager.getAccountsByType(MainApp.getAccountType())[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(MainApp.getAccountType())[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;
-        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(MainApp.getAuthTokenType());
-            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);
-            CustomButton btn = (CustomButton) 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 {
-            //WebdavClient webdav = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext());
-
-            ArrayList<String> local = new ArrayList<String>();
-            ArrayList<String> remote = new ArrayList<String>();
-            
-            /* 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);
-            }
-            */
-            
-            // this checks the mimeType 
-            for (Parcelable mStream : mStreamsToUpload) {
-                
-                Uri uri = (Uri) mStream;
-                if (uri !=null) {
-                    if (uri.getScheme().equals("content")) {
-                        
-                       String mimeType = getContentResolver().getType(uri);
-                       
-                       if (mimeType.contains("image")) {
-                           String[] CONTENT_PROJECTION = { Images.Media.DATA, Images.Media.DISPLAY_NAME, Images.Media.MIME_TYPE, Images.Media.SIZE};
-                           Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null, null, null);
-                           c.moveToFirst();
-                           int index = c.getColumnIndex(Images.Media.DATA);
-                           String data = c.getString(index);
-                           local.add(data);
-                           remote.add(mUploadPath + c.getString(c.getColumnIndex(Images.Media.DISPLAY_NAME)));
-                       
-                       }
-                       else if (mimeType.contains("video")) {
-                           String[] CONTENT_PROJECTION = { Video.Media.DATA, Video.Media.DISPLAY_NAME, Video.Media.MIME_TYPE, Video.Media.SIZE, Video.Media.DATE_MODIFIED };
-                           Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null, null, null);
-                           c.moveToFirst();
-                           int index = c.getColumnIndex(Video.Media.DATA);
-                           String data = c.getString(index);
-                           local.add(data);
-                           remote.add(mUploadPath + c.getString(c.getColumnIndex(Video.Media.DISPLAY_NAME)));
-                          
-                       }
-                       else if (mimeType.contains("audio")) {
-                           String[] CONTENT_PROJECTION = { Audio.Media.DATA, Audio.Media.DISPLAY_NAME, Audio.Media.MIME_TYPE, Audio.Media.SIZE };
-                           Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null, null, null);
-                           c.moveToFirst();
-                           int index = c.getColumnIndex(Audio.Media.DATA);
-                           String data = c.getString(index);
-                           local.add(data);
-                           remote.add(mUploadPath + c.getString(c.getColumnIndex(Audio.Media.DISPLAY_NAME)));
-                        
-                       }
-                       else {
-                           String filePath = Uri.decode(uri.toString()).replace(uri.getScheme() + "://", "");
-                           // cut everything whats before mnt. It occured to me that sometimes apps send their name into the URI
-                           if (filePath.contains("mnt")) {
-                              String splitedFilePath[] = filePath.split("/mnt");
-                              filePath = splitedFilePath[1];
-                           }
-                           final File file = new File(filePath);
-                           local.add(file.getAbsolutePath());
-                           remote.add(mUploadPath + file.getName());
-                       }
-                        
-                    } else if (uri.getScheme().equals("file")) {
-                        String filePath = Uri.decode(uri.toString()).replace(uri.getScheme() + "://", "");
-                        if (filePath.contains("mnt")) {
-                           String splitedFilePath[] = filePath.split("/mnt");
-                           filePath = splitedFilePath[1];
-                        }
-                        final File file = new File(filePath);
-                        local.add(file.getAbsolutePath());
-                        remote.add(mUploadPath + file.getName());
-                    }
-                    else {
-                        throw new SecurityException();
-                    }
-                }
-                else {
-                    throw new SecurityException();
-                }
-           
-            Intent intent = new Intent(getApplicationContext(), FileUploader.class);
-            intent.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES);
-            intent.putExtra(FileUploader.KEY_LOCAL_FILE, local.toArray(new String[local.size()]));
-            intent.putExtra(FileUploader.KEY_REMOTE_FILE, remote.toArray(new String[remote.size()]));
-            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();            
-        }
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/authentication/AccountAuthenticator.java b/src/de/mobilcom/debitel/cloud/android/authentication/AccountAuthenticator.java
deleted file mode 100644 (file)
index 1c27e89..0000000
+++ /dev/null
@@ -1,358 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.authentication;
-
-import android.accounts.*;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Handler;
-import android.widget.Toast;
-
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.R;
-
-
-/**
- *  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.
-     */
-    /* These constants are now in MainApp
-         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 AUTH_TOKEN_TYPE_SAML_WEB_SSO_SESSION_COOKIE = "owncloud.saml.web_sso.session_cookie";
-    */
-    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 de.mobilcom.debitel.cloud.android.authentication.AuthenticatorActivity.KEY_OC_BASE_URL} and
-     * {@link de.mobilcom.debitel.cloud.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 de.mobilcom.debitel.cloud.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";
-    /**
-     * Flag signaling if the ownCloud server can be accessed with session cookies from SAML-based web single-sign-on.
-     */
-    public static final String KEY_SUPPORTS_SAML_WEB_SSO = "oc_supports_saml_web_sso";
-    
-    private static final String TAG = AccountAuthenticator.class.getSimpleName();
-    
-    private Context mContext;
-    
-    private Handler mHandler;
-
-    public AccountAuthenticator(Context context) {
-        super(context);
-        mContext = context;
-        mHandler = new Handler();
-    }
-
-    /**
-     * {@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);
-        
-        final Bundle bundle = new Bundle();
-        
-        AccountManager accountManager = AccountManager.get(mContext);
-        Account[] accounts = accountManager.getAccountsByType(MainApp.getAccountType());
-        
-        if (mContext.getResources().getBoolean(R.bool.multiaccount_support) || accounts.length < 1) {
-            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);
-            
-            bundle.putParcelable(AccountManager.KEY_INTENT, intent);
-        
-        } else {
-
-            // Return an error
-            bundle.putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION);
-            final String message = String.format(mContext.getString(R.string.auth_unsupported_multiaccount), mContext.getString(R.string.app_name)); 
-            bundle.putString(AccountManager.KEY_ERROR_MESSAGE, message);
-           
-            mHandler.post(new Runnable() {
-
-                @Override
-                public void run() {
-                    Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
-                }
-            });
-            
-        }
-        
-        return bundle;
-    }
-
-    /**\r
-     * {@inheritDoc}\r
-     */\r
-    @Override\r
-    public Bundle confirmCredentials(AccountAuthenticatorResponse response,\r
-            Account account, Bundle options) throws NetworkErrorException {\r
-        try {\r
-            validateAccountType(account.type);\r
-        } catch (AuthenticatorException e) {\r
-            Log_OC.e(TAG, "Failed to validate account type " + account.type + ": "\r
-                    + e.getMessage());\r
-            e.printStackTrace();\r
-            return e.getFailureBundle();\r
-        }\r
-        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_LOGIN_OPTIONS, options);\r
-\r
-        setIntentFlags(intent);\r
-\r
-        Bundle resultBundle = new Bundle();\r
-        resultBundle.putParcelable(AccountManager.KEY_INTENT, intent);\r
-        return resultBundle;\r
-    }\r
-\r
-    @Override\r
-    public Bundle editProperties(AccountAuthenticatorResponse response,\r
-            String accountType) {\r
-        return null;\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(MainApp.getAuthTokenTypePass())) {
-            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, MainApp.getAccountType());
-            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_ENFORCED_UPDATE, true);
-        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(MainApp.getAccountType())) {
-            throw new UnsupportedAccountTypeException();
-        }
-    }
-
-    private void validateAuthTokenType(String authTokenType)\r
-            throws UnsupportedAuthTokenTypeException {\r
-        if (!authTokenType.equals(MainApp.getAuthTokenType()) &&\r
-            !authTokenType.equals(MainApp.getAuthTokenTypePass()) &&\r
-            !authTokenType.equals(MainApp.getAuthTokenTypeAccessToken()) &&\r
-            !authTokenType.equals(MainApp.getAuthTokenTypeRefreshToken()) &&
-            !authTokenType.equals(MainApp.getAuthTokenTypeSamlSessionCookie())) {\r
-            throw new UnsupportedAuthTokenTypeException();\r
-        }\r
-    }\r
-\r
-    public static class AuthenticatorException extends Exception {\r
-        private static final long serialVersionUID = 1L;\r
-        private Bundle mFailureBundle;\r
-\r
-        public AuthenticatorException(int code, String errorMsg) {\r
-            mFailureBundle = new Bundle();\r
-            mFailureBundle.putInt(AccountManager.KEY_ERROR_CODE, code);\r
-            mFailureBundle\r
-                    .putString(AccountManager.KEY_ERROR_MESSAGE, errorMsg);\r
-        }\r
-\r
-        public Bundle getFailureBundle() {\r
-            return mFailureBundle;\r
-        }\r
-    }\r
-\r
-    public static class UnsupportedAccountTypeException extends\r
-            AuthenticatorException {\r
-        private static final long serialVersionUID = 1L;\r
-\r
-        public UnsupportedAccountTypeException() {\r
-            super(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,\r
-                    "Unsupported account type");\r
-        }\r
-    }\r
-\r
-    public static class UnsupportedAuthTokenTypeException extends\r
-            AuthenticatorException {\r
-        private static final long serialVersionUID = 1L;\r
-\r
-        public UnsupportedAuthTokenTypeException() {\r
-            super(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,\r
-                    "Unsupported auth token type");\r
-        }\r
-    }\r
-\r
-    public static class UnsupportedFeaturesException extends\r
-            AuthenticatorException {\r
-        public static final long serialVersionUID = 1L;\r
-\r
-        public UnsupportedFeaturesException() {\r
-            super(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,\r
-                    "Unsupported features");\r
-        }\r
-    }\r
-\r
-    public static class AccessDeniedException extends AuthenticatorException {\r
-        public AccessDeniedException(int code, String errorMsg) {\r
-            super(AccountManager.ERROR_CODE_INVALID_RESPONSE, "Access Denied");\r
-        }\r
-\r
-        private static final long serialVersionUID = 1L;\r
-\r
-    }\r
-}\r
diff --git a/src/de/mobilcom/debitel/cloud/android/authentication/AccountAuthenticatorActivity.java b/src/de/mobilcom/debitel/cloud/android/authentication/AccountAuthenticatorActivity.java
deleted file mode 100644 (file)
index 9a6c980..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package de.mobilcom.debitel.cloud.android.authentication;
-
-import android.accounts.AccountAuthenticatorResponse;
-import android.accounts.AccountManager;
-import android.os.Bundle;
-
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-
-
-/*
- * Base class for implementing an Activity that is used to help implement an AbstractAccountAuthenticator. 
- * If the AbstractAccountAuthenticator needs to use an activity to handle the request then it can have the activity extend 
- * AccountAuthenticatorActivity. The AbstractAccountAuthenticator passes in the response to the intent using the following:
- * intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
- * 
- * The activity then sets the result that is to be handed to the response via setAccountAuthenticatorResult(android.os.Bundle). 
- * This result will be sent as the result of the request when the activity finishes. If this is never set or if it is set to null 
- * then error AccountManager.ERROR_CODE_CANCELED will be called on the response.
- */
-
-public class AccountAuthenticatorActivity extends SherlockFragmentActivity {
-
-    private AccountAuthenticatorResponse mAccountAuthenticatorResponse = null;
-    private Bundle mResultBundle = null;
-
-
-    /**
-     * Set the result that is to be sent as the result of the request that caused this Activity to be launched.
-     * If result is null or this method is never called then the request will be canceled.
-     * 
-     * @param result this is returned as the result of the AbstractAccountAuthenticator request
-     */
-    public final void setAccountAuthenticatorResult(Bundle result) {
-        mResultBundle = result;
-    }
-
-    /**
-     * Retreives the AccountAuthenticatorResponse from either the intent of the icicle, if the
-     * icicle is non-zero.
-     * @param icicle the save instance data of this Activity, may be null
-     */
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        mAccountAuthenticatorResponse =
-                getIntent().getParcelableExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
-
-        if (mAccountAuthenticatorResponse != null) {
-            mAccountAuthenticatorResponse.onRequestContinued();
-        }
-    }
-    
-    /**
-     * Sends the result or a Constants.ERROR_CODE_CANCELED error if a result isn't present.
-     */
-    public void finish() {
-        if (mAccountAuthenticatorResponse != null) {
-            // send the result bundle back if set, otherwise send an error.
-            if (mResultBundle != null) {
-                mAccountAuthenticatorResponse.onResult(mResultBundle);
-            } else {
-                mAccountAuthenticatorResponse.onError(AccountManager.ERROR_CODE_CANCELED,
-                        "canceled");
-            }
-            mAccountAuthenticatorResponse = null;
-        }
-        super.finish();
-    }
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/authentication/AccountAuthenticatorService.java b/src/de/mobilcom/debitel/cloud/android/authentication/AccountAuthenticatorService.java
deleted file mode 100644 (file)
index 9f00be1..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.authentication;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-
-public class AccountAuthenticatorService extends Service {
-
-    private AccountAuthenticator mAuthenticator;
-    //static final public String ACCOUNT_TYPE = "owncloud";
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mAuthenticator = new AccountAuthenticator(this);
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return mAuthenticator.getIBinder();
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/authentication/AccountUtils.java b/src/de/mobilcom/debitel/cloud/android/authentication/AccountUtils.java
deleted file mode 100644 (file)
index db0ea0d..0000000
+++ /dev/null
@@ -1,220 +0,0 @@
-/* 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 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 de.mobilcom.debitel.cloud.android.authentication;\r
-\r
-import de.mobilcom.debitel.cloud.android.MainApp;\r
-import de.mobilcom.debitel.cloud.android.utils.OwnCloudVersion;\r
-\r
-import android.accounts.Account;\r
-import android.accounts.AccountManager;\r
-import android.accounts.AccountsException;\r
-import android.content.Context;\r
-import android.content.SharedPreferences;\r
-import android.preference.PreferenceManager;\r
-\r
-public class AccountUtils {\r
-    public static final String WEBDAV_PATH_1_2 = "/webdav/owncloud.php";\r
-    public static final String WEBDAV_PATH_2_0 = "/files/webdav.php";\r
-    public static final String WEBDAV_PATH_4_0 = "/remote.php/webdav";\r
-    private static final String ODAV_PATH = "/remote.php/odav";\r
-    private static final String SAML_SSO_PATH = "/remote.php/webdav";\r
-    public static final String CARDDAV_PATH_2_0 = "/apps/contacts/carddav.php";\r
-    public static final String CARDDAV_PATH_4_0 = "/remote/carddav.php";\r
-    public static final String STATUS_PATH = "/status.php";\r
-\r
-    /**\r
-     * Can be used to get the currently selected ownCloud {@link Account} in the\r
-     * application preferences.\r
-     * \r
-     * @param   context     The current application {@link Context}\r
-     * @return              The ownCloud {@link Account} currently saved in preferences, or the first \r
-     *                      {@link Account} available, if valid (still registered in the system as ownCloud \r
-     *                      account). If none is available and valid, returns null.\r
-     */\r
-    public static Account getCurrentOwnCloudAccount(Context context) {\r
-        Account[] ocAccounts = AccountManager.get(context).getAccountsByType(\r
-                MainApp.getAccountType());\r
-        Account defaultAccount = null;\r
-\r
-        SharedPreferences appPreferences = PreferenceManager\r
-                .getDefaultSharedPreferences(context);\r
-        String accountName = appPreferences\r
-                .getString("select_oc_account", null);\r
-\r
-        // account validation: the saved account MUST be in the list of ownCloud Accounts known by the AccountManager\r
-        if (accountName != null) {\r
-            for (Account account : ocAccounts) {\r
-                if (account.name.equals(accountName)) {\r
-                    defaultAccount = account;\r
-                    break;\r
-                }\r
-            }\r
-        }\r
-        \r
-        if (defaultAccount == null && ocAccounts.length != 0) {\r
-            // take first account as fallback\r
-            defaultAccount = ocAccounts[0];\r
-        }\r
-\r
-        return defaultAccount;\r
-    }\r
-\r
-    \r
-    public static boolean exists(Account account, Context context) {\r
-        Account[] ocAccounts = AccountManager.get(context).getAccountsByType(\r
-                MainApp.getAccountType());\r
-\r
-        if (account != null && account.name != null) {\r
-            for (Account ac : ocAccounts) {\r
-                if (ac.name.equals(account.name)) {\r
-                    return true;\r
-                }\r
-            }\r
-        }\r
-        return false;\r
-    }\r
-    \r
-\r
-    /**\r
-     * Checks, whether or not there are any ownCloud accounts setup.\r
-     * \r
-     * @return true, if there is at least one account.\r
-     */\r
-    public static boolean accountsAreSetup(Context context) {\r
-        AccountManager accMan = AccountManager.get(context);\r
-        Account[] accounts = accMan\r
-                .getAccountsByType(MainApp.getAccountType());\r
-        return accounts.length > 0;\r
-    }\r
-    \r
-    \r
-    public static boolean setCurrentOwnCloudAccount(Context context, String accountName) {\r
-        boolean result = false;\r
-        if (accountName != null) {\r
-            Account[] ocAccounts = AccountManager.get(context).getAccountsByType(\r
-                    MainApp.getAccountType());\r
-            boolean found = false;\r
-            for (Account account : ocAccounts) {\r
-                found = (account.name.equals(accountName));\r
-                if (found) {\r
-                    SharedPreferences.Editor appPrefs = PreferenceManager\r
-                            .getDefaultSharedPreferences(context).edit();\r
-                    appPrefs.putString("select_oc_account", accountName);\r
-    \r
-                    appPrefs.commit();\r
-                    result = true;\r
-                    break;\r
-                }\r
-            }\r
-        }\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * \r
-     * @param version version of owncloud\r
-     * @return webdav path for given OC version, null if OC version unknown\r
-     */\r
-    public static String getWebdavPath(OwnCloudVersion version, boolean supportsOAuth, boolean supportsSamlSso) {\r
-        if (version != null) {\r
-            if (supportsOAuth) {\r
-                return ODAV_PATH;\r
-            }\r
-            if (supportsSamlSso) {\r
-                return SAML_SSO_PATH;\r
-            }\r
-            if (version.compareTo(OwnCloudVersion.owncloud_v4) >= 0)\r
-                return WEBDAV_PATH_4_0;\r
-            if (version.compareTo(OwnCloudVersion.owncloud_v3) >= 0\r
-                    || version.compareTo(OwnCloudVersion.owncloud_v2) >= 0)\r
-                return WEBDAV_PATH_2_0;\r
-            if (version.compareTo(OwnCloudVersion.owncloud_v1) >= 0)\r
-                return WEBDAV_PATH_1_2;\r
-        }\r
-        return null;\r
-    }\r
-    \r
-    /**\r
-     * Returns the proper URL path to access the WebDAV interface of an ownCloud server,\r
-     * according to its version and the authorization method used.\r
-     * \r
-     * @param   version         Version of ownCloud server.\r
-     * @param   authTokenType   Authorization token type, matching some of the AUTH_TOKEN_TYPE_* constants in {@link AccountAuthenticator}. \r
-     * @return                  WebDAV path for given OC version and authorization method, null if OC version is unknown.\r
-     */\r
-    public static String getWebdavPath(OwnCloudVersion version, String authTokenType) {\r
-        if (version != null) {\r
-            if (MainApp.getAuthTokenTypeAccessToken().equals(authTokenType)) {\r
-                return ODAV_PATH;\r
-            }\r
-            if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(authTokenType)) {\r
-                return SAML_SSO_PATH;\r
-            }\r
-            if (version.compareTo(OwnCloudVersion.owncloud_v4) >= 0)\r
-                return WEBDAV_PATH_4_0;\r
-            if (version.compareTo(OwnCloudVersion.owncloud_v3) >= 0\r
-                    || version.compareTo(OwnCloudVersion.owncloud_v2) >= 0)\r
-                return WEBDAV_PATH_2_0;\r
-            if (version.compareTo(OwnCloudVersion.owncloud_v1) >= 0)\r
-                return WEBDAV_PATH_1_2;\r
-        }\r
-        return null;\r
-    }\r
-    \r
-    /**\r
-     * Constructs full url to host and webdav resource basing on host version\r
-     * @param context\r
-     * @param account\r
-     * @return url or null on failure\r
-     * @throws AccountNotFoundException     When 'account' is unknown for the AccountManager\r
-     */\r
-    public static String constructFullURLForAccount(Context context, Account account) throws AccountNotFoundException {\r
-        AccountManager ama = AccountManager.get(context);\r
-        String baseurl = ama.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL);\r
-        String strver  = ama.getUserData(account, AccountAuthenticator.KEY_OC_VERSION);\r
-        boolean supportsOAuth = (ama.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null);\r
-        boolean supportsSamlSso = (ama.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO) != null);\r
-        OwnCloudVersion ver = new OwnCloudVersion(strver);\r
-        String webdavpath = getWebdavPath(ver, supportsOAuth, supportsSamlSso);\r
-\r
-        if (baseurl == null || webdavpath == null) \r
-            throw new AccountNotFoundException(account, "Account not found", null);\r
-        \r
-        return baseurl + webdavpath;\r
-    }\r
-    \r
-    \r
-    public static class AccountNotFoundException extends AccountsException {\r
-        \r
-        /** Generated - should be refreshed every time the class changes!! */\r
-        private static final long serialVersionUID = -9013287181793186830L;\r
-        \r
-        private Account mFailedAccount; \r
-                \r
-        public AccountNotFoundException(Account failedAccount, String message, Throwable cause) {\r
-            super(message, cause);\r
-            mFailedAccount = failedAccount;\r
-        }\r
-        \r
-        public Account getFailedAccount() {\r
-            return mFailedAccount;\r
-        }\r
-    }\r
-\r
-}\r
diff --git a/src/de/mobilcom/debitel/cloud/android/authentication/AuthenticatorActivity.java b/src/de/mobilcom/debitel/cloud/android/authentication/AuthenticatorActivity.java
deleted file mode 100644 (file)
index 3709779..0000000
+++ /dev/null
@@ -1,1672 +0,0 @@
-/* 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 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 de.mobilcom.debitel.cloud.android.authentication;\r
-\r
-import android.accounts.Account;\r
-import android.accounts.AccountManager;\r
-import android.app.AlertDialog;\r
-import android.app.Dialog;\r
-import android.app.ProgressDialog;\r
-import android.content.ContentResolver;\r
-import android.content.DialogInterface;\r
-import android.content.Intent;\r
-import android.content.SharedPreferences;\r
-import android.graphics.Rect;\r
-import android.graphics.drawable.Drawable;\r
-import android.net.Uri;\r
-import android.os.Bundle;\r
-import android.os.Handler;\r
-import android.preference.PreferenceManager;\r
-import android.support.v4.app.Fragment;\r
-import android.text.Editable;\r
-import android.text.InputType;\r
-import android.text.TextWatcher;\r
-import android.view.KeyEvent;\r
-import android.view.MotionEvent;\r
-import android.view.View;\r
-import android.view.View.OnFocusChangeListener;\r
-import android.view.View.OnTouchListener;\r
-import android.view.Window;\r
-import android.view.inputmethod.EditorInfo;\r
-import android.widget.Button;\r
-import android.widget.CheckBox;\r
-import android.widget.EditText;\r
-import android.widget.TextView;\r
-import android.widget.TextView.OnEditorActionListener;\r
-\r
-import com.actionbarsherlock.app.SherlockDialogFragment;\r
-\r
-import de.mobilcom.debitel.cloud.android.Log_OC;\r
-import de.mobilcom.debitel.cloud.android.MainApp;\r
-import de.mobilcom.debitel.cloud.android.R;\r
-import de.mobilcom.debitel.cloud.android.authentication.SsoWebViewClient.SsoWebViewClientListener;\r
-import de.mobilcom.debitel.cloud.android.network.OwnCloudClientUtils;\r
-import de.mobilcom.debitel.cloud.android.operations.ExistenceCheckOperation;\r
-import de.mobilcom.debitel.cloud.android.operations.OAuth2GetAccessToken;\r
-import de.mobilcom.debitel.cloud.android.operations.OnRemoteOperationListener;\r
-import de.mobilcom.debitel.cloud.android.operations.OwnCloudServerCheckOperation;\r
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperation;\r
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult;\r
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;\r
-import de.mobilcom.debitel.cloud.android.ui.CustomButton;\r
-import de.mobilcom.debitel.cloud.android.ui.dialog.SamlWebViewDialog;\r
-import de.mobilcom.debitel.cloud.android.ui.dialog.SslValidatorDialog;\r
-import de.mobilcom.debitel.cloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener;\r
-import de.mobilcom.debitel.cloud.android.utils.OwnCloudVersion;\r
-\r
-import eu.alefzero.webdav.WebdavClient;\r
-\r
-/**\r
- * This Activity is used to add an ownCloud account to the App\r
- * \r
- * @author Bartek Przybylski\r
- * @author David A. Velasco\r
- */\r
-public class AuthenticatorActivity extends AccountAuthenticatorActivity\r
-implements  OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeListener, OnEditorActionListener, SsoWebViewClientListener{\r
-\r
-    private static final String TAG = AuthenticatorActivity.class.getSimpleName();\r
-\r
-    public static final String EXTRA_ACCOUNT = "ACCOUNT";\r
-    public static final String EXTRA_USER_NAME = "USER_NAME";\r
-    public static final String EXTRA_HOST_NAME = "HOST_NAME";\r
-    public static final String EXTRA_ACTION = "ACTION";\r
-    public static final String EXTRA_ENFORCED_UPDATE = "ENFORCE_UPDATE";\r
-\r
-    private static final String KEY_AUTH_MESSAGE_VISIBILITY = "AUTH_MESSAGE_VISIBILITY";\r
-    private static final String KEY_AUTH_MESSAGE_TEXT = "AUTH_MESSAGE_TEXT";\r
-    private static final String KEY_HOST_URL_TEXT = "HOST_URL_TEXT";\r
-    private static final String KEY_OC_VERSION = "OC_VERSION";\r
-    private static final String KEY_ACCOUNT = "ACCOUNT";\r
-    private static final String KEY_SERVER_VALID = "SERVER_VALID";\r
-    private static final String KEY_SERVER_CHECKED = "SERVER_CHECKED";\r
-    private static final String KEY_SERVER_CHECK_IN_PROGRESS = "SERVER_CHECK_IN_PROGRESS"; \r
-    private static final String KEY_SERVER_STATUS_TEXT = "SERVER_STATUS_TEXT";\r
-    private static final String KEY_SERVER_STATUS_ICON = "SERVER_STATUS_ICON";\r
-    private static final String KEY_IS_SSL_CONN = "IS_SSL_CONN";\r
-    private static final String KEY_PASSWORD_VISIBLE = "PASSWORD_VISIBLE";\r
-    private static final String KEY_AUTH_STATUS_TEXT = "AUTH_STATUS_TEXT";\r
-    private static final String KEY_AUTH_STATUS_ICON = "AUTH_STATUS_ICON";\r
-    private static final String KEY_REFRESH_BUTTON_ENABLED = "KEY_REFRESH_BUTTON_ENABLED";\r
-    \r
-    private static final String KEY_OC_USERNAME_EQUALS = "oc_username=";\r
-\r
-    private static final String AUTH_ON = "on";\r
-    private static final String AUTH_OFF = "off";\r
-    private static final String AUTH_OPTIONAL = "optional";\r
-    \r
-    private static final int DIALOG_LOGIN_PROGRESS = 0;\r
-    private static final int DIALOG_SSL_VALIDATOR = 1;\r
-    private static final int DIALOG_CERT_NOT_SAVED = 2;\r
-    private static final int DIALOG_OAUTH2_LOGIN_PROGRESS = 3;\r
-\r
-    public static final byte ACTION_CREATE = 0;\r
-    public static final byte ACTION_UPDATE_TOKEN = 1;\r
-\r
-    private static final String TAG_SAML_DIALOG = "samlWebViewDialog";\r
-    \r
-    private String mHostBaseUrl;\r
-    private OwnCloudVersion mDiscoveredVersion;\r
-\r
-    private String mAuthMessageText;\r
-    private int mAuthMessageVisibility, mServerStatusText, mServerStatusIcon;\r
-    private boolean mServerIsChecked, mServerIsValid, mIsSslConn;\r
-    private int mAuthStatusText, mAuthStatusIcon;    \r
-    private TextView mAuthStatusLayout;\r
-\r
-    private final Handler mHandler = new Handler();\r
-    private Thread mOperationThread;\r
-    private OwnCloudServerCheckOperation mOcServerChkOperation;\r
-    private ExistenceCheckOperation mAuthCheckOperation;\r
-    private RemoteOperationResult mLastSslUntrustedServerResult;\r
-\r
-    private Uri mNewCapturedUriFromOAuth2Redirection;\r
-\r
-    private AccountManager mAccountMgr;\r
-    private boolean mJustCreated;\r
-    private byte mAction;\r
-    private Account mAccount;\r
-\r
-    private TextView mAuthMessage;\r
-    \r
-    private EditText mHostUrlInput;\r
-    private boolean mHostUrlInputEnabled;\r
-    private View mRefreshButton;\r
-\r
-    private String mAuthTokenType;\r
-    \r
-    private EditText mUsernameInput;\r
-    private EditText mPasswordInput;\r
-    \r
-    private CheckBox mOAuth2Check;\r
-    \r
-    private TextView mOAuthAuthEndpointText;\r
-    private TextView mOAuthTokenEndpointText;\r
-    \r
-    private SamlWebViewDialog mSamlDialog;\r
-    \r
-    private View mOkButton;\r
-    \r
-    private String mAuthToken;\r
-    \r
-    private boolean mResumed; // Control if activity is resumed\r
-\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * \r
-     * IMPORTANT ENTRY POINT 1: activity is shown to the user\r
-     */\r
-    @Override\r
-    protected void onCreate(Bundle savedInstanceState) {\r
-        super.onCreate(savedInstanceState);\r
-        getWindow().requestFeature(Window.FEATURE_NO_TITLE);\r
-\r
-        /// set view and get references to view elements\r
-        setContentView(R.layout.account_setup);\r
-        mAuthMessage = (TextView) findViewById(R.id.auth_message);\r
-        mHostUrlInput = (EditText) findViewById(R.id.hostUrlInput);\r
-        mHostUrlInput.setText(getString(R.string.server_url));  // valid although R.string.server_url is an empty string\r
-        mUsernameInput = (EditText) findViewById(R.id.account_username);\r
-        mPasswordInput = (EditText) findViewById(R.id.account_password);\r
-        mOAuthAuthEndpointText = (TextView)findViewById(R.id.oAuthEntryPoint_1);\r
-        mOAuthTokenEndpointText = (TextView)findViewById(R.id.oAuthEntryPoint_2);\r
-        mOAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check);\r
-        mOkButton = (CustomButton) findViewById(R.id.buttonOK);\r
-        mAuthStatusLayout = (TextView) findViewById(R.id.auth_status_text); \r
-        \r
-        /// set Host Url Input Enabled\r
-        mHostUrlInputEnabled = getResources().getBoolean(R.bool.show_server_url_input);\r
-        \r
-\r
-        /// complete label for 'register account' button\r
-        Button b = (Button) findViewById(R.id.account_register);\r
-        if (b != null) {\r
-            b.setText(String.format(getString(R.string.auth_register), getString(R.string.app_name)));            \r
-        }\r
-\r
-//        /// complete background of 'OK' button\r
-//        boolean customButtons = getResources().getBoolean(R.bool.custom_buttons);\r
-//        if (customButtons)\r
-//            mOkButton.setBackgroundResource(R.drawable.btn_default);\r
-        \r
-        /// initialization\r
-        mAccountMgr = AccountManager.get(this);\r
-        mNewCapturedUriFromOAuth2Redirection = null;\r
-        mAction = getIntent().getByteExtra(EXTRA_ACTION, ACTION_CREATE); \r
-        mAccount = null;\r
-        mHostBaseUrl = "";\r
-        boolean refreshButtonEnabled = false;\r
-        \r
-        // URL input configuration applied\r
-        if (!mHostUrlInputEnabled)\r
-        {\r
-            findViewById(R.id.hostUrlFrame).setVisibility(View.GONE);\r
-            mRefreshButton = findViewById(R.id.centeredRefreshButton);\r
-\r
-        } else {\r
-            mRefreshButton = findViewById(R.id.embeddedRefreshButton);\r
-        }\r
-\r
-        if (savedInstanceState == null) {\r
-            mResumed = false;\r
-            /// connection state and info\r
-            mAuthMessageVisibility = View.GONE;\r
-            mServerStatusText = mServerStatusIcon = 0;\r
-            mServerIsValid = false;\r
-            mServerIsChecked = false;\r
-            mIsSslConn = false;\r
-            mAuthStatusText = mAuthStatusIcon = 0;\r
-\r
-            /// retrieve extras from intent\r
-            mAccount = getIntent().getExtras().getParcelable(EXTRA_ACCOUNT);\r
-            if (mAccount != null) {\r
-                String ocVersion = mAccountMgr.getUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION);\r
-                if (ocVersion != null) {\r
-                    mDiscoveredVersion = new OwnCloudVersion(ocVersion);\r
-                }\r
-                mHostBaseUrl = normalizeUrl(mAccountMgr.getUserData(mAccount, AccountAuthenticator.KEY_OC_BASE_URL));\r
-                mHostUrlInput.setText(mHostBaseUrl);\r
-                String userName = mAccount.name.substring(0, mAccount.name.lastIndexOf('@'));\r
-                mUsernameInput.setText(userName);\r
-            }\r
-            initAuthorizationMethod();  // checks intent and setup.xml to determine mCurrentAuthorizationMethod\r
-            mJustCreated = true;\r
-            \r
-            if (mAction == ACTION_UPDATE_TOKEN || !mHostUrlInputEnabled) {\r
-                checkOcServer(); \r
-            }\r
-            \r
-        } else {\r
-            mResumed = true;\r
-            /// connection state and info\r
-            mAuthMessageVisibility = savedInstanceState.getInt(KEY_AUTH_MESSAGE_VISIBILITY);\r
-            mAuthMessageText = savedInstanceState.getString(KEY_AUTH_MESSAGE_TEXT);\r
-            mServerIsValid = savedInstanceState.getBoolean(KEY_SERVER_VALID);\r
-            mServerIsChecked = savedInstanceState.getBoolean(KEY_SERVER_CHECKED);\r
-            mServerStatusText = savedInstanceState.getInt(KEY_SERVER_STATUS_TEXT);\r
-            mServerStatusIcon = savedInstanceState.getInt(KEY_SERVER_STATUS_ICON);\r
-            mIsSslConn = savedInstanceState.getBoolean(KEY_IS_SSL_CONN);\r
-            mAuthStatusText = savedInstanceState.getInt(KEY_AUTH_STATUS_TEXT);\r
-            mAuthStatusIcon = savedInstanceState.getInt(KEY_AUTH_STATUS_ICON);\r
-            if (savedInstanceState.getBoolean(KEY_PASSWORD_VISIBLE, false)) {\r
-                showPassword();\r
-            }\r
-            \r
-            /// server data\r
-            String ocVersion = savedInstanceState.getString(KEY_OC_VERSION);\r
-            if (ocVersion != null) {\r
-                mDiscoveredVersion = new OwnCloudVersion(ocVersion);\r
-            }\r
-            mHostBaseUrl = savedInstanceState.getString(KEY_HOST_URL_TEXT);\r
-\r
-            // account data, if updating\r
-            mAccount = savedInstanceState.getParcelable(KEY_ACCOUNT);\r
-            mAuthTokenType = savedInstanceState.getString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE);\r
-            if (mAuthTokenType == null) {\r
-                mAuthTokenType =  MainApp.getAuthTokenTypePass();\r
-                \r
-            }\r
-\r
-            // check if server check was interrupted by a configuration change\r
-            if (savedInstanceState.getBoolean(KEY_SERVER_CHECK_IN_PROGRESS, false)) {\r
-                checkOcServer();\r
-            }            \r
-            \r
-            // refresh button enabled\r
-            refreshButtonEnabled = savedInstanceState.getBoolean(KEY_REFRESH_BUTTON_ENABLED);\r
-            \r
-\r
-        }\r
-\r
-        if (mAuthMessageVisibility== View.VISIBLE) {\r
-            showAuthMessage(mAuthMessageText);\r
-        }\r
-        else {\r
-            hideAuthMessage();\r
-        }\r
-        adaptViewAccordingToAuthenticationMethod();\r
-        showServerStatus();\r
-        showAuthStatus();\r
-        \r
-        if (mAction == ACTION_UPDATE_TOKEN) {\r
-            /// lock things that should not change\r
-            mHostUrlInput.setEnabled(false);\r
-            mHostUrlInput.setFocusable(false);\r
-            mUsernameInput.setEnabled(false);\r
-            mUsernameInput.setFocusable(false);\r
-            mOAuth2Check.setVisibility(View.GONE);\r
-        }\r
-        \r
-        //if (mServerIsChecked && !mServerIsValid && mRefreshButtonEnabled) showRefreshButton();\r
-        if (mServerIsChecked && !mServerIsValid && refreshButtonEnabled) showRefreshButton();\r
-        mOkButton.setEnabled(mServerIsValid); // state not automatically recovered in configuration changes\r
-\r
-        if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType) || \r
-                !AUTH_OPTIONAL.equals(getString(R.string.auth_method_oauth2))) {\r
-            mOAuth2Check.setVisibility(View.GONE);\r
-        }\r
-\r
-        mPasswordInput.setText("");     // clean password to avoid social hacking (disadvantage: password in removed if the device is turned aside)\r
-\r
-        /// bind view elements to listeners and other friends\r
-        mHostUrlInput.setOnFocusChangeListener(this);\r
-        mHostUrlInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);\r
-        mHostUrlInput.setOnEditorActionListener(this);\r
-        mHostUrlInput.addTextChangedListener(new TextWatcher() {\r
-\r
-            @Override\r
-            public void afterTextChanged(Editable s) {\r
-                if (!mHostBaseUrl.equals(normalizeUrl(mHostUrlInput.getText().toString()))) {\r
-                    mOkButton.setEnabled(false);\r
-                }\r
-            }\r
-\r
-            @Override\r
-            public void beforeTextChanged(CharSequence s, int start, int count, int after) {\r
-            }\r
-\r
-            @Override\r
-            public void onTextChanged(CharSequence s, int start, int before, int count) {\r
-                if (!mResumed) {\r
-                    mAuthStatusIcon = 0;\r
-                    mAuthStatusText = 0;\r
-                    showAuthStatus();                    \r
-                }\r
-                mResumed = false;\r
-            }\r
-        });\r
-        \r
-        mPasswordInput.setOnFocusChangeListener(this);\r
-        mPasswordInput.setImeOptions(EditorInfo.IME_ACTION_DONE);\r
-        mPasswordInput.setOnEditorActionListener(this);
-        mPasswordInput.setOnTouchListener(new RightDrawableOnTouchListener() {\r
-            @Override\r
-            public boolean onDrawableTouch(final MotionEvent event) {\r
-                if (event.getAction() == MotionEvent.ACTION_UP) {\r
-                    AuthenticatorActivity.this.onViewPasswordClick();\r
-                }\r
-                return true;\r
-            }\r
-        });\r
-        \r
-        findViewById(R.id.scroll).setOnTouchListener(new OnTouchListener() {\r
-            @Override\r
-            public boolean onTouch(View view, MotionEvent event) {\r
-                if (event.getAction() == MotionEvent.ACTION_DOWN) {\r
-                    if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType) &&\r
-                            mHostUrlInput.hasFocus()) {\r
-                        checkOcServer();\r
-                    }\r
-                }\r
-                return false;\r
-            }\r
-        });\r
-    }\r
-    \r
-   \r
-\r
-    private void initAuthorizationMethod() {\r
-        boolean oAuthRequired = false;\r
-        boolean samlWebSsoRequired = false;\r
-\r
-        mAuthTokenType = getIntent().getExtras().getString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE);\r
-        mAccount = getIntent().getExtras().getParcelable(EXTRA_ACCOUNT);\r
-        \r
-        // TODO could be a good moment to validate the received token type, if not null\r
-        \r
-        if (mAuthTokenType == null) {    \r
-            if (mAccount != null) {\r
-                /// same authentication method than the one used to create the account to update\r
-                oAuthRequired = (mAccountMgr.getUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null);\r
-                samlWebSsoRequired = (mAccountMgr.getUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO) != null);\r
-            \r
-            } else {\r
-                /// use the one set in setup.xml\r
-                oAuthRequired = AUTH_ON.equals(getString(R.string.auth_method_oauth2));\r
-                samlWebSsoRequired = AUTH_ON.equals(getString(R.string.auth_method_saml_web_sso));            \r
-            }\r
-            if (oAuthRequired) {\r
-                mAuthTokenType = MainApp.getAuthTokenTypeAccessToken();\r
-            } else if (samlWebSsoRequired) {\r
-                mAuthTokenType = MainApp.getAuthTokenTypeSamlSessionCookie();\r
-            } else {\r
-                mAuthTokenType = MainApp.getAuthTokenTypePass();\r
-            }\r
-        }\r
-    \r
-        if (mAccount != null) {\r
-            String userName = mAccount.name.substring(0, mAccount.name.lastIndexOf('@'));\r
-            mUsernameInput.setText(userName);\r
-        }\r
-        \r
-        mOAuth2Check.setChecked(MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType));\r
-        \r
-    }\r
-\r
-    /**\r
-     * Saves relevant state before {@link #onPause()}\r
-     * \r
-     * Do NOT save {@link #mNewCapturedUriFromOAuth2Redirection}; it keeps a temporal flag, intended to defer the \r
-     * processing of the redirection caught in {@link #onNewIntent(Intent)} until {@link #onResume()} \r
-     * \r
-     * See {@link #loadSavedInstanceState(Bundle)}\r
-     */\r
-    @Override\r
-    protected void onSaveInstanceState(Bundle outState) {\r
-        super.onSaveInstanceState(outState);\r
-\r
-        /// connection state and info\r
-        outState.putInt(KEY_AUTH_MESSAGE_VISIBILITY, mAuthMessage.getVisibility());\r
-        outState.putString(KEY_AUTH_MESSAGE_TEXT, mAuthMessage.getText().toString());\r
-        outState.putInt(KEY_SERVER_STATUS_TEXT, mServerStatusText);\r
-        outState.putInt(KEY_SERVER_STATUS_ICON, mServerStatusIcon);\r
-        outState.putBoolean(KEY_SERVER_VALID, mServerIsValid);\r
-        outState.putBoolean(KEY_SERVER_CHECKED, mServerIsChecked);\r
-        outState.putBoolean(KEY_SERVER_CHECK_IN_PROGRESS, (!mServerIsValid && mOcServerChkOperation != null));\r
-        outState.putBoolean(KEY_IS_SSL_CONN, mIsSslConn);\r
-        outState.putBoolean(KEY_PASSWORD_VISIBLE, isPasswordVisible());\r
-        outState.putInt(KEY_AUTH_STATUS_ICON, mAuthStatusIcon);\r
-        outState.putInt(KEY_AUTH_STATUS_TEXT, mAuthStatusText);\r
-\r
-        /// server data\r
-        if (mDiscoveredVersion != null) {\r
-            outState.putString(KEY_OC_VERSION, mDiscoveredVersion.toString());\r
-        }\r
-        outState.putString(KEY_HOST_URL_TEXT, mHostBaseUrl);\r
-\r
-        /// account data, if updating\r
-        if (mAccount != null) {\r
-            outState.putParcelable(KEY_ACCOUNT, mAccount);\r
-        }\r
-        outState.putString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE, mAuthTokenType);\r
-        \r
-        // refresh button enabled\r
-        outState.putBoolean(KEY_REFRESH_BUTTON_ENABLED, (mRefreshButton.getVisibility() == View.VISIBLE));\r
-        \r
-\r
-    }\r
-\r
-\r
-    /**\r
-     * The redirection triggered by the OAuth authentication server as response to the GET AUTHORIZATION request\r
-     * is caught here.\r
-     * \r
-     * To make this possible, this activity needs to be qualified with android:launchMode = "singleTask" in the\r
-     * AndroidManifest.xml file.\r
-     */\r
-    @Override\r
-    protected void onNewIntent (Intent intent) {\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
-        }\r
-    }\r
-\r
-\r
-    /**\r
-     * The redirection triggered by the OAuth authentication server as response to the GET AUTHORIZATION, and \r
-     * deferred in {@link #onNewIntent(Intent)}, is processed here.\r
-     */\r
-    @Override\r
-    protected void onResume() {\r
-        super.onResume();\r
-        if (mAction == ACTION_UPDATE_TOKEN && mJustCreated && getIntent().getBooleanExtra(EXTRA_ENFORCED_UPDATE, false)) {\r
-            if (MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType)) {\r
-                //Toast.makeText(this, R.string.auth_expired_oauth_token_toast, Toast.LENGTH_LONG).show();\r
-                showAuthMessage(getString(R.string.auth_expired_oauth_token_toast));\r
-            } else if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) {\r
-                //Toast.makeText(this, R.string.auth_expired_saml_sso_token_toast, Toast.LENGTH_LONG).show();\r
-                showAuthMessage(getString(R.string.auth_expired_saml_sso_token_toast));\r
-            } else {\r
-                //Toast.makeText(this, R.string.auth_expired_basic_auth_toast, Toast.LENGTH_LONG).show();\r
-                showAuthMessage(getString(R.string.auth_expired_basic_auth_toast));\r
-            }\r
-        }\r
-\r
-        if (mNewCapturedUriFromOAuth2Redirection != null) {\r
-            getOAuth2AccessTokenFromCapturedRedirection();            \r
-        }\r
-\r
-        mJustCreated = false;\r
-        \r
-    }\r
-\r
-\r
-    /**\r
-     * Parses the redirection with the response to the GET AUTHORIZATION request to the \r
-     * oAuth server and requests for the access token (GET ACCESS TOKEN)\r
-     */\r
-    private void getOAuth2AccessTokenFromCapturedRedirection() {\r
-        /// Parse data from OAuth redirection\r
-        String queryParameters = mNewCapturedUriFromOAuth2Redirection.getQuery();\r
-        mNewCapturedUriFromOAuth2Redirection = null;\r
-\r
-        /// Showing the dialog with instructions for the user.\r
-        showDialog(DIALOG_OAUTH2_LOGIN_PROGRESS);\r
-\r
-        /// GET ACCESS TOKEN to the oAuth server \r
-        RemoteOperation operation = new OAuth2GetAccessToken(   getString(R.string.oauth2_client_id), \r
-                getString(R.string.oauth2_redirect_uri),       \r
-                getString(R.string.oauth2_grant_type),\r
-                queryParameters);\r
-        //WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(getString(R.string.oauth2_url_endpoint_access)), getApplicationContext());\r
-        WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mOAuthTokenEndpointText.getText().toString().trim()), getApplicationContext(), true);\r
-        operation.execute(client, this, mHandler);\r
-    }\r
-\r
-\r
-\r
-    /**\r
-     * Handles the change of focus on the text inputs for the server URL and the password\r
-     */\r
-    public void onFocusChange(View view, boolean hasFocus) {\r
-        if (view.getId() == R.id.hostUrlInput) {   \r
-            if (!hasFocus) {\r
-                onUrlInputFocusLost((TextView) view);\r
-            }\r
-            else {\r
-                hideRefreshButton();\r
-            }\r
-\r
-        } else if (view.getId() == R.id.account_password) {\r
-            onPasswordFocusChanged((TextView) view, hasFocus);\r
-        }\r
-    }\r
-\r
-\r
-    /**\r
-     * Handles changes in focus on the text input for the server URL.\r
-     * \r
-     * IMPORTANT ENTRY POINT 2: When (!hasFocus), user wrote the server URL and changed to \r
-     * other field. The operation to check the existence of the server in the entered URL is\r
-     * started. \r
-     * \r
-     * When hasFocus:    user 'comes back' to write again the server URL.\r
-     * \r
-     * @param hostInput     TextView with the URL input field receiving the change of focus.\r
-     */\r
-    private void onUrlInputFocusLost(TextView hostInput) {\r
-        if (!mHostBaseUrl.equals(normalizeUrl(mHostUrlInput.getText().toString()))) {\r
-            checkOcServer();\r
-        } else {\r
-            mOkButton.setEnabled(mServerIsValid);\r
-            if (!mServerIsValid) {\r
-                showRefreshButton();\r
-            }\r
-        }\r
-    }\r
-\r
-\r
-    private void checkOcServer() {\r
-        String uri = trimUrlWebdav(mHostUrlInput.getText().toString().trim());\r
-        \r
-        if (!mHostUrlInputEnabled){\r
-            uri = getString(R.string.server_url);\r
-        }\r
-        \r
-        mServerIsValid = false;\r
-        mServerIsChecked = false;\r
-        mOkButton.setEnabled(false);\r
-        mDiscoveredVersion = null;\r
-        hideRefreshButton();\r
-        if (uri.length() != 0) {\r
-            mServerStatusText = R.string.auth_testing_connection;\r
-            mServerStatusIcon = R.drawable.progress_small;\r
-            showServerStatus();\r
-            mOcServerChkOperation = new  OwnCloudServerCheckOperation(uri, this);\r
-            WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(uri), this, true);\r
-            mOperationThread = mOcServerChkOperation.execute(client, this, mHandler);\r
-        } else {\r
-            mServerStatusText = 0;\r
-            mServerStatusIcon = 0;\r
-            showServerStatus();\r
-        }\r
-    }\r
-\r
-\r
-    /**\r
-     * Handles changes in focus on the text input for the password (basic authorization).\r
-     * \r
-     * When (hasFocus), the button to toggle password visibility is shown.\r
-     * \r
-     * When (!hasFocus), the button is made invisible and the password is hidden.\r
-     * \r
-     * @param passwordInput    TextView with the password input field receiving the change of focus.\r
-     * @param hasFocus          'True' if focus is received, 'false' if is lost\r
-     */\r
-    private void onPasswordFocusChanged(TextView passwordInput, boolean hasFocus) {\r
-        if (hasFocus) {\r
-            showViewPasswordButton();\r
-        } else {\r
-            hidePassword();\r
-            hidePasswordButton();\r
-        }\r
-    }\r
-\r
-\r
-    private void showViewPasswordButton() {\r
-        //int drawable = android.R.drawable.ic_menu_view;\r
-        int drawable = R.drawable.ic_view;\r
-        if (isPasswordVisible()) {\r
-            //drawable = android.R.drawable.ic_secure;\r
-            drawable = R.drawable.ic_hide;\r
-        }\r
-        mPasswordInput.setCompoundDrawablesWithIntrinsicBounds(0, 0, drawable, 0);\r
-    }\r
-\r
-    private boolean isPasswordVisible() {\r
-        return ((mPasswordInput.getInputType() & InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);\r
-    }\r
-    \r
-    private void hidePasswordButton() {\r
-        mPasswordInput.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);\r
-    }\r
-\r
-    private void showPassword() {\r
-        mPasswordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);\r
-        showViewPasswordButton();\r
-    }\r
-    \r
-    private void hidePassword() {\r
-        mPasswordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);\r
-        showViewPasswordButton();\r
-    }\r
-    \r
-    \r
-    /**\r
-     * Cancels the authenticator activity\r
-     * \r
-     * IMPORTANT ENTRY POINT 3: Never underestimate the importance of cancellation\r
-     * \r
-     * This method is bound in the layout/acceoun_setup.xml resource file.\r
-     * \r
-     * @param view      Cancel button\r
-     */\r
-    public void onCancelClick(View view) {\r
-        setResult(RESULT_CANCELED);     // TODO review how is this related to AccountAuthenticator (debugging)\r
-        finish();\r
-    }\r
-\r
-\r
-\r
-    /**\r
-     * Checks the credentials of the user in the root of the ownCloud server\r
-     * before creating a new local account.\r
-     * \r
-     * For basic authorization, a check of existence of the root folder is\r
-     * performed.\r
-     * \r
-     * For OAuth, starts the flow to get an access token; the credentials test \r
-     * is postponed until it is available.\r
-     * \r
-     * IMPORTANT ENTRY POINT 4\r
-     * \r
-     * @param view      OK button\r
-     */\r
-    public void onOkClick(View view) {\r
-        // this check should be unnecessary\r
-        if (mDiscoveredVersion == null || !mDiscoveredVersion.isVersionValid()  || mHostBaseUrl == null || mHostBaseUrl.length() == 0) {\r
-            mServerStatusIcon = R.drawable.common_error;\r
-            mServerStatusText = R.string.auth_wtf_reenter_URL;\r
-            showServerStatus();\r
-            mOkButton.setEnabled(false);\r
-            Log_OC.wtf(TAG,  "The user was allowed to click 'connect' to an unchecked server!!");\r
-            return;\r
-        }\r
-\r
-        if (MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType)) {\r
-            startOauthorization();\r
-        } else if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) { \r
-            startSamlBasedFederatedSingleSignOnAuthorization();\r
-        } else {\r
-            checkBasicAuthorization();\r
-        }\r
-    }\r
-\r
-\r
-    /**\r
-     * Tests the credentials entered by the user performing a check of existence on \r
-     * the root folder of the ownCloud server.\r
-     */\r
-    private void checkBasicAuthorization() {\r
-        /// get the path to the root folder through WebDAV from the version server\r
-        String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType);\r
-\r
-        /// get basic credentials entered by user\r
-        String username = mUsernameInput.getText().toString();\r
-        String password = mPasswordInput.getText().toString();\r
-\r
-        /// be gentle with the user\r
-        showDialog(DIALOG_LOGIN_PROGRESS);\r
-\r
-        /// test credentials accessing the root folder\r
-        mAuthCheckOperation = new  ExistenceCheckOperation("", this, false);\r
-        WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, true);\r
-        client.setBasicCredentials(username, password);\r
-        mOperationThread = mAuthCheckOperation.execute(client, this, mHandler);\r
-    }\r
-\r
-\r
-    /**\r
-     * Starts the OAuth 'grant type' flow to get an access token, with \r
-     * a GET AUTHORIZATION request to the BUILT-IN authorization server. \r
-     */\r
-    private void startOauthorization() {\r
-        // be gentle with the user\r
-        mAuthStatusIcon = R.drawable.progress_small;\r
-        mAuthStatusText = R.string.oauth_login_connection;\r
-        showAuthStatus();\r
-        \r
-\r
-        // GET AUTHORIZATION request\r
-        //Uri uri = Uri.parse(getString(R.string.oauth2_url_endpoint_auth));\r
-        Uri uri = Uri.parse(mOAuthAuthEndpointText.getText().toString().trim());\r
-        Uri.Builder uriBuilder = uri.buildUpon();\r
-        uriBuilder.appendQueryParameter(OAuth2Constants.KEY_RESPONSE_TYPE, getString(R.string.oauth2_response_type));\r
-        uriBuilder.appendQueryParameter(OAuth2Constants.KEY_REDIRECT_URI, getString(R.string.oauth2_redirect_uri));   \r
-        uriBuilder.appendQueryParameter(OAuth2Constants.KEY_CLIENT_ID, getString(R.string.oauth2_client_id));\r
-        uriBuilder.appendQueryParameter(OAuth2Constants.KEY_SCOPE, getString(R.string.oauth2_scope));\r
-        //uriBuilder.appendQueryParameter(OAuth2Constants.KEY_STATE, whateverwewant);\r
-        uri = uriBuilder.build();\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
-\r
-\r
-    /**\r
-     * Starts the Web Single Sign On flow to get access to the root folder\r
-     * in the server.\r
-     */\r
-    private void startSamlBasedFederatedSingleSignOnAuthorization() {\r
-        // be gentle with the user\r
-        mAuthStatusIcon = R.drawable.progress_small;\r
-        mAuthStatusText = R.string.auth_connecting_auth_server;\r
-        showAuthStatus();\r
-        showDialog(DIALOG_LOGIN_PROGRESS);\r
-        \r
-        /// get the path to the root folder through WebDAV from the version server\r
-        String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType);\r
-\r
-        /// test credentials accessing the root folder\r
-        mAuthCheckOperation = new  ExistenceCheckOperation("", this, false);\r
-        WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, false);\r
-        mOperationThread = mAuthCheckOperation.execute(client, this, mHandler);\r
-      \r
-    }\r
-\r
-    /**\r
-     * Callback method invoked when a RemoteOperation executed by this Activity finishes.\r
-     * \r
-     * Dispatches the operation flow to the right method.\r
-     */\r
-    @Override\r
-    public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {\r
-\r
-        if (operation instanceof OwnCloudServerCheckOperation) {\r
-            onOcServerCheckFinish((OwnCloudServerCheckOperation) operation, result);\r
-\r
-        } else if (operation instanceof OAuth2GetAccessToken) {\r
-            onGetOAuthAccessTokenFinish((OAuth2GetAccessToken)operation, result);\r
-\r
-        } else if (operation instanceof ExistenceCheckOperation)  {\r
-            if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) {\r
-                onSamlBasedFederatedSingleSignOnAuthorizationStart(operation, result);\r
-                \r
-            } else {\r
-                onAuthorizationCheckFinish((ExistenceCheckOperation)operation, result);\r
-            }\r
-        }\r
-    }\r
-    \r
-    \r
-    private void onSamlBasedFederatedSingleSignOnAuthorizationStart(RemoteOperation operation, RemoteOperationResult result) {\r
-        try {\r
-            dismissDialog(DIALOG_LOGIN_PROGRESS);\r
-        } catch (IllegalArgumentException e) {\r
-            // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens\r
-        }\r
-        
-        //if (result.isTemporalRedirection() && result.isIdPRedirection()) {\r
-        if (result.isIdPRedirection()) {
-            String url = result.getRedirectedLocation();\r
-            String targetUrl = mHostBaseUrl + AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType);\r
-            \r
-            // Show dialog\r
-            mSamlDialog = SamlWebViewDialog.newInstance(url, targetUrl);            \r
-            mSamlDialog.show(getSupportFragmentManager(), TAG_SAML_DIALOG);\r
-            \r
-            mAuthStatusIcon = 0;\r
-            mAuthStatusText = 0;\r
-            \r
-        } else {\r
-            mAuthStatusIcon = R.drawable.common_error;\r
-            mAuthStatusText = R.string.auth_unsupported_auth_method;\r
-            \r
-        }\r
-        showAuthStatus();\r
-    }\r
-\r
-\r
-    /**\r
-     * Processes the result of the server check performed when the user finishes the enter of the\r
-     * server URL.\r
-     * \r
-     * @param operation     Server check performed.\r
-     * @param result        Result of the check.\r
-     */\r
-    private void onOcServerCheckFinish(OwnCloudServerCheckOperation operation, RemoteOperationResult result) {\r
-        if (operation.equals(mOcServerChkOperation)) {\r
-            /// save result state\r
-            mServerIsChecked = true;\r
-            mServerIsValid = result.isSuccess();\r
-            mIsSslConn = (result.getCode() == ResultCode.OK_SSL);\r
-            mOcServerChkOperation = null;\r
-\r
-            /// update status icon and text\r
-            if (mServerIsValid) {\r
-                hideRefreshButton();\r
-            } else {\r
-                showRefreshButton();\r
-            }\r
-            updateServerStatusIconAndText(result);\r
-            showServerStatus();\r
-\r
-            /// very special case (TODO: move to a common place for all the remote operations)\r
-            if (result.getCode() == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) {\r
-                mLastSslUntrustedServerResult = result;\r
-                showDialog(DIALOG_SSL_VALIDATOR); \r
-            }\r
-\r
-            /// retrieve discovered version and normalize server URL\r
-            mDiscoveredVersion = operation.getDiscoveredVersion();\r
-            mHostBaseUrl = normalizeUrl(mHostUrlInput.getText().toString());\r
-\r
-            /// allow or not the user try to access the server\r
-            mOkButton.setEnabled(mServerIsValid);\r
-\r
-        }   // else nothing ; only the last check operation is considered; \r
-        // multiple can be triggered if the user amends a URL before a previous check can be triggered\r
-    }\r
-\r
-\r
-    private String normalizeUrl(String url) {\r
-        if (url != null && url.length() > 0) {\r
-            url = url.trim();\r
-            if (!url.toLowerCase().startsWith("http://") &&\r
-                    !url.toLowerCase().startsWith("https://")) {\r
-                if (mIsSslConn) {\r
-                    url = "https://" + url;\r
-                } else {\r
-                    url = "http://" + url;\r
-                }\r
-            }\r
-\r
-            // OC-208: Add suffix remote.php/webdav to normalize (OC-34)            \r
-            url = trimUrlWebdav(url);\r
-\r
-            if (url.endsWith("/")) {\r
-                url = url.substring(0, url.length() - 1);\r
-            }\r
-\r
-        }\r
-        return (url != null ? url : "");\r
-    }\r
-\r
-\r
-    private String trimUrlWebdav(String url){       \r
-        if(url.toLowerCase().endsWith(AccountUtils.WEBDAV_PATH_4_0)){\r
-            url = url.substring(0, url.length() - AccountUtils.WEBDAV_PATH_4_0.length());             \r
-        } else if(url.toLowerCase().endsWith(AccountUtils.WEBDAV_PATH_2_0)){\r
-            url = url.substring(0, url.length() - AccountUtils.WEBDAV_PATH_2_0.length());             \r
-        } else if (url.toLowerCase().endsWith(AccountUtils.WEBDAV_PATH_1_2)){\r
-            url = url.substring(0, url.length() - AccountUtils.WEBDAV_PATH_1_2.length());             \r
-        } \r
-        return (url != null ? url : "");\r
-    }\r
-    \r
-    \r
-    /**\r
-     * Chooses the right icon and text to show to the user for the received operation result.\r
-     * \r
-     * @param result    Result of a remote operation performed in this activity\r
-     */\r
-    private void updateServerStatusIconAndText(RemoteOperationResult result) {\r
-        mServerStatusIcon = R.drawable.common_error;    // the most common case in the switch below\r
-\r
-        switch (result.getCode()) {\r
-        case OK_SSL:\r
-            mServerStatusIcon = android.R.drawable.ic_secure;\r
-            mServerStatusText = R.string.auth_secure_connection;\r
-            break;\r
-\r
-        case OK_NO_SSL:\r
-        case OK:\r
-            if (mHostUrlInput.getText().toString().trim().toLowerCase().startsWith("http://") ) {\r
-                mServerStatusText = R.string.auth_connection_established;\r
-                mServerStatusIcon = R.drawable.ic_ok;\r
-            } else {\r
-                mServerStatusText = R.string.auth_nossl_plain_ok_title;\r
-                mServerStatusIcon = android.R.drawable.ic_partial_secure;\r
-            }\r
-            break;\r
-\r
-        case NO_NETWORK_CONNECTION:\r
-            mServerStatusIcon = R.drawable.no_network;\r
-            mServerStatusText = R.string.auth_no_net_conn_title;\r
-            break;\r
-\r
-        case SSL_RECOVERABLE_PEER_UNVERIFIED:\r
-            mServerStatusText = R.string.auth_ssl_unverified_server_title;\r
-            break;\r
-        case BAD_OC_VERSION:\r
-            mServerStatusText = R.string.auth_bad_oc_version_title;\r
-            break;\r
-        case WRONG_CONNECTION:\r
-            mServerStatusText = R.string.auth_wrong_connection_title;\r
-            break;\r
-        case TIMEOUT:\r
-            mServerStatusText = R.string.auth_timeout_title;\r
-            break;\r
-        case INCORRECT_ADDRESS:\r
-            mServerStatusText = R.string.auth_incorrect_address_title;\r
-            break;\r
-        case SSL_ERROR:\r
-            mServerStatusText = R.string.auth_ssl_general_error_title;\r
-            break;\r
-        case UNAUTHORIZED:\r
-            mServerStatusText = R.string.auth_unauthorized;\r
-            break;\r
-        case HOST_NOT_AVAILABLE:\r
-            mServerStatusText = R.string.auth_unknown_host_title;\r
-            break;\r
-        case INSTANCE_NOT_CONFIGURED:\r
-            mServerStatusText = R.string.auth_not_configured_title;\r
-            break;\r
-        case FILE_NOT_FOUND:\r
-            mServerStatusText = R.string.auth_incorrect_path_title;\r
-            break;\r
-        case OAUTH2_ERROR:\r
-            mServerStatusText = R.string.auth_oauth_error;\r
-            break;\r
-        case OAUTH2_ERROR_ACCESS_DENIED:\r
-            mServerStatusText = R.string.auth_oauth_error_access_denied;\r
-            break;\r
-        case UNHANDLED_HTTP_CODE:\r
-        case UNKNOWN_ERROR:\r
-            mServerStatusText = R.string.auth_unknown_error_title;\r
-            break;\r
-        default:\r
-            mServerStatusText = 0;\r
-            mServerStatusIcon = 0;\r
-        }\r
-    }\r
-\r
-\r
-    /**\r
-     * Chooses the right icon and text to show to the user for the received operation result.\r
-     * \r
-     * @param result    Result of a remote operation performed in this activity\r
-     */\r
-    private void updateAuthStatusIconAndText(RemoteOperationResult result) {\r
-        mAuthStatusIcon = R.drawable.common_error;    // the most common case in the switch below\r
-\r
-        switch (result.getCode()) {\r
-        case OK_SSL:\r
-            mAuthStatusIcon = android.R.drawable.ic_secure;\r
-            mAuthStatusText = R.string.auth_secure_connection;\r
-            break;\r
-\r
-        case OK_NO_SSL:\r
-        case OK:\r
-            if (mHostUrlInput.getText().toString().trim().toLowerCase().startsWith("http://") ) {\r
-                mAuthStatusText = R.string.auth_connection_established;\r
-                mAuthStatusIcon = R.drawable.ic_ok;\r
-            } else {\r
-                mAuthStatusText = R.string.auth_nossl_plain_ok_title;\r
-                mAuthStatusIcon = android.R.drawable.ic_partial_secure;\r
-            }\r
-            break;\r
-\r
-        case NO_NETWORK_CONNECTION:\r
-            mAuthStatusIcon = R.drawable.no_network;\r
-            mAuthStatusText = R.string.auth_no_net_conn_title;\r
-            break;\r
-\r
-        case SSL_RECOVERABLE_PEER_UNVERIFIED:\r
-            mAuthStatusText = R.string.auth_ssl_unverified_server_title;\r
-            break;\r
-        case BAD_OC_VERSION:\r
-            mAuthStatusText = R.string.auth_bad_oc_version_title;\r
-            break;\r
-        case WRONG_CONNECTION:\r
-            mAuthStatusText = R.string.auth_wrong_connection_title;\r
-            break;\r
-        case TIMEOUT:\r
-            mAuthStatusText = R.string.auth_timeout_title;\r
-            break;\r
-        case INCORRECT_ADDRESS:\r
-            mAuthStatusText = R.string.auth_incorrect_address_title;\r
-            break;\r
-        case SSL_ERROR:\r
-            mAuthStatusText = R.string.auth_ssl_general_error_title;\r
-            break;\r
-        case UNAUTHORIZED:\r
-            mAuthStatusText = R.string.auth_unauthorized;\r
-            break;\r
-        case HOST_NOT_AVAILABLE:\r
-            mAuthStatusText = R.string.auth_unknown_host_title;\r
-            break;\r
-        case INSTANCE_NOT_CONFIGURED:\r
-            mAuthStatusText = R.string.auth_not_configured_title;\r
-            break;\r
-        case FILE_NOT_FOUND:\r
-            mAuthStatusText = R.string.auth_incorrect_path_title;\r
-            break;\r
-        case OAUTH2_ERROR:\r
-            mAuthStatusText = R.string.auth_oauth_error;\r
-            break;\r
-        case OAUTH2_ERROR_ACCESS_DENIED:\r
-            mAuthStatusText = R.string.auth_oauth_error_access_denied;\r
-            break;\r
-        case ACCOUNT_NOT_NEW:\r
-            mAuthStatusText = R.string.auth_account_not_new;\r
-            break;\r
-        case ACCOUNT_NOT_THE_SAME:\r
-            mAuthStatusText = R.string.auth_account_not_the_same;\r
-            break;\r
-        case UNHANDLED_HTTP_CODE:\r
-        case UNKNOWN_ERROR:\r
-            mAuthStatusText = R.string.auth_unknown_error_title;\r
-            break;\r
-        default:\r
-            mAuthStatusText = 0;\r
-            mAuthStatusIcon = 0;\r
-        }\r
-    }\r
-\r
-\r
-    /**\r
-     * Processes the result of the request for and access token send \r
-     * to an OAuth authorization server.\r
-     * \r
-     * @param operation     Operation performed requesting the access token.\r
-     * @param result        Result of the operation.\r
-     */\r
-    private void onGetOAuthAccessTokenFinish(OAuth2GetAccessToken operation, RemoteOperationResult result) {\r
-        try {\r
-            dismissDialog(DIALOG_OAUTH2_LOGIN_PROGRESS);\r
-        } catch (IllegalArgumentException e) {\r
-            // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens\r
-        }\r
-\r
-        String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType);\r
-        if (result.isSuccess() && webdav_path != null) {\r
-            /// be gentle with the user\r
-            showDialog(DIALOG_LOGIN_PROGRESS);\r
-\r
-            /// time to test the retrieved access token on the ownCloud server\r
-            mAuthToken = ((OAuth2GetAccessToken)operation).getResultTokenMap().get(OAuth2Constants.KEY_ACCESS_TOKEN);\r
-            Log_OC.d(TAG, "Got ACCESS TOKEN: " + mAuthToken);\r
-            mAuthCheckOperation = new ExistenceCheckOperation("", this, false);\r
-            WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, true);\r
-            client.setBearerCredentials(mAuthToken);\r
-            mAuthCheckOperation.execute(client, this, mHandler);\r
-\r
-        } else {\r
-            updateAuthStatusIconAndText(result);\r
-            showAuthStatus();\r
-            Log_OC.d(TAG, "Access failed: " + result.getLogMessage());\r
-        }\r
-    }\r
-\r
-\r
-    /**\r
-     * Processes the result of the access check performed to try the user credentials.\r
-     * \r
-     * Creates a new account through the AccountManager.\r
-     * \r
-     * @param operation     Access check performed.\r
-     * @param result        Result of the operation.\r
-     */\r
-    private void onAuthorizationCheckFinish(ExistenceCheckOperation operation, RemoteOperationResult result) {\r
-        try {\r
-            dismissDialog(DIALOG_LOGIN_PROGRESS);\r
-        } catch (IllegalArgumentException e) {\r
-            // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens\r
-        }\r
-\r
-        if (result.isSuccess()) {\r
-            Log_OC.d(TAG, "Successful access - time to save the account");\r
-\r
-            boolean success = false;\r
-            if (mAction == ACTION_CREATE) {\r
-                success = createAccount();\r
-\r
-            } else {\r
-                success = updateToken();\r
-            }\r
-\r
-            if (success) {\r
-                finish();\r
-            }\r
-\r
-        } else if (result.isServerFail() || result.isException()) {\r
-            /// if server fail or exception in authorization, the UI is updated as when a server check failed\r
-            mServerIsChecked = true;\r
-            mServerIsValid = false;\r
-            mIsSslConn = false;\r
-            mOcServerChkOperation = null;\r
-            mDiscoveredVersion = null;\r
-            mHostBaseUrl = normalizeUrl(mHostUrlInput.getText().toString());\r
-\r
-            // update status icon and text\r
-            updateServerStatusIconAndText(result);\r
-            showServerStatus();\r
-            mAuthStatusIcon = 0;\r
-            mAuthStatusText = 0;\r
-            showAuthStatus();\r
-            \r
-            // update input controls state\r
-            showRefreshButton();\r
-            mOkButton.setEnabled(false);\r
-\r
-            // very special case (TODO: move to a common place for all the remote operations) (dangerous here?)\r
-            if (result.getCode() == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) {\r
-                mLastSslUntrustedServerResult = result;\r
-                showDialog(DIALOG_SSL_VALIDATOR); \r
-            }\r
-\r
-        } else {    // authorization fail due to client side - probably wrong credentials\r
-            updateAuthStatusIconAndText(result);\r
-            showAuthStatus();\r
-            Log_OC.d(TAG, "Access failed: " + result.getLogMessage());\r
-        }\r
-\r
-    }\r
-\r
-\r
-    /**\r
-     * Sets the proper response to get that the Account Authenticator that started this activity saves \r
-     * a new authorization token for mAccount.\r
-     */\r
-    private boolean updateToken() {\r
-        Bundle response = new Bundle();\r
-        response.putString(AccountManager.KEY_ACCOUNT_NAME, mAccount.name);\r
-        response.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccount.type);\r
-        \r
-        if (MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType)) { \r
-            response.putString(AccountManager.KEY_AUTHTOKEN, mAuthToken);\r
-            // the next line is necessary; by now, notifications are calling directly to the AuthenticatorActivity to update, without AccountManager intervention\r
-            mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken);\r
-            \r
-        } else if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) {\r
-            String username = getUserNameForSamlSso();\r
-            if (!mUsernameInput.getText().toString().equals(username)) {\r
-                // fail - not a new account, but an existing one; disallow\r
-                RemoteOperationResult result = new RemoteOperationResult(ResultCode.ACCOUNT_NOT_THE_SAME); \r
-                updateAuthStatusIconAndText(result);\r
-                showAuthStatus();\r
-                Log_OC.d(TAG, result.getLogMessage());\r
-                \r
-                return false;\r
-            }\r
-            \r
-            response.putString(AccountManager.KEY_AUTHTOKEN, mAuthToken);\r
-            // the next line is necessary; by now, notifications are calling directly to the AuthenticatorActivity to update, without AccountManager intervention\r
-            mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken);\r
-            \r
-        } else {\r
-            response.putString(AccountManager.KEY_AUTHTOKEN, mPasswordInput.getText().toString());\r
-            mAccountMgr.setPassword(mAccount, mPasswordInput.getText().toString());\r
-        }\r
-        setAccountAuthenticatorResult(response);\r
-        \r
-        return true;\r
-    }\r
-\r
-\r
-    /**\r
-     * Creates a new account through the Account Authenticator that started this activity. \r
-     * \r
-     * This makes the account permanent.\r
-     * \r
-     * TODO Decide how to name the OAuth accounts\r
-     */\r
-    private boolean createAccount() {\r
-        /// create and save new ownCloud account\r
-        boolean isOAuth = MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType);\r
-        boolean isSaml =  MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType);\r
-\r
-        Uri uri = Uri.parse(mHostBaseUrl);\r
-        String username = mUsernameInput.getText().toString().trim();\r
-        if (isSaml) {\r
-            username = getUserNameForSamlSso();\r
-            \r
-        } else if (isOAuth) {\r
-            username = "OAuth_user" + (new java.util.Random(System.currentTimeMillis())).nextLong();\r
-        }            \r
-        String accountName = username + "@" + uri.getHost();\r
-        if (uri.getPort() >= 0) {\r
-            accountName += ":" + uri.getPort();\r
-        }\r
-        mAccount = new Account(accountName, MainApp.getAccountType());\r
-        if (AccountUtils.exists(mAccount, getApplicationContext())) {\r
-            // fail - not a new account, but an existing one; disallow\r
-            RemoteOperationResult result = new RemoteOperationResult(ResultCode.ACCOUNT_NOT_NEW); \r
-            updateAuthStatusIconAndText(result);\r
-            showAuthStatus();\r
-            Log_OC.d(TAG, result.getLogMessage());\r
-            return false;\r
-            \r
-        } else {\r
-        \r
-            if (isOAuth || isSaml) {\r
-                mAccountMgr.addAccountExplicitly(mAccount, "", null);  // with external authorizations, the password is never input in the app\r
-            } else {\r
-                mAccountMgr.addAccountExplicitly(mAccount, mPasswordInput.getText().toString(), null);\r
-            }\r
-    \r
-            /// add the new account as default in preferences, if there is none already\r
-            Account defaultAccount = AccountUtils.getCurrentOwnCloudAccount(this);\r
-            if (defaultAccount == null) {\r
-                SharedPreferences.Editor editor = PreferenceManager\r
-                        .getDefaultSharedPreferences(this).edit();\r
-                editor.putString("select_oc_account", accountName);\r
-                editor.commit();\r
-            }\r
-    \r
-            /// prepare result to return to the Authenticator\r
-            //  TODO check again what the Authenticator makes with it; probably has the same effect as addAccountExplicitly, but it's not well done\r
-            final Intent intent = new Intent();       \r
-            intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE,    MainApp.getAccountType());\r
-            intent.putExtra(AccountManager.KEY_ACCOUNT_NAME,    mAccount.name);\r
-            /*if (!isOAuth)\r
-                intent.putExtra(AccountManager.KEY_AUTHTOKEN,   MainApp.getAccountType()); */\r
-            intent.putExtra(AccountManager.KEY_USERDATA,        username);\r
-            if (isOAuth || isSaml) {\r
-                mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken);\r
-            }\r
-            /// add user data to the new account; TODO probably can be done in the last parameter addAccountExplicitly, or in KEY_USERDATA\r
-            mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION,    mDiscoveredVersion.toString());\r
-            mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_OC_BASE_URL,   mHostBaseUrl);\r
-            if (isSaml) {\r
-                mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO, "TRUE"); \r
-            } else if (isOAuth) {\r
-                mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_OAUTH2, "TRUE");  \r
-            }\r
-    \r
-            setAccountAuthenticatorResult(intent.getExtras());\r
-            setResult(RESULT_OK, intent);\r
-    \r
-            /// immediately request for the synchronization of the new account\r
-            Bundle bundle = new Bundle();\r
-            bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);\r
-            ContentResolver.requestSync(mAccount, MainApp.getAuthTokenType(), bundle);\r
-            syncAccount();\r
-//          Bundle bundle = new Bundle();\r
-//          bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);\r
-//          ContentResolver.requestSync(mAccount, MainApp.getAuthTokenType(), bundle);\r
-            return true;\r
-        }\r
-    }\r
-\r
-    \r
-    private String getUserNameForSamlSso() {\r
-        if (mAuthToken != null) {\r
-            String [] cookies = mAuthToken.split(";");\r
-            for (int i=0; i<cookies.length; i++) {\r
-                if (cookies[i].startsWith(KEY_OC_USERNAME_EQUALS )) {\r
-                    String value = Uri.decode(cookies[i].substring(KEY_OC_USERNAME_EQUALS.length()));\r
-                    return value;\r
-                }\r
-            }\r
-        }\r
-        return "";\r
-    }\r
-\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * \r
-     * Necessary to update the contents of the SSL Dialog\r
-     * \r
-     * TODO move to some common place for all possible untrusted SSL failures\r
-     */\r
-    @Override\r
-    protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {\r
-        switch (id) {\r
-        case DIALOG_LOGIN_PROGRESS:\r
-        case DIALOG_CERT_NOT_SAVED:\r
-        case DIALOG_OAUTH2_LOGIN_PROGRESS:\r
-            break;\r
-        case DIALOG_SSL_VALIDATOR: {\r
-            ((SslValidatorDialog)dialog).updateResult(mLastSslUntrustedServerResult);\r
-            break;\r
-        }\r
-        default:\r
-            Log_OC.e(TAG, "Incorrect dialog called with id = " + id);\r
-        }\r
-    }\r
-\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     */\r
-    @Override\r
-    protected Dialog onCreateDialog(int id) {\r
-        Dialog dialog = null;\r
-        switch (id) {\r
-        case DIALOG_LOGIN_PROGRESS: {\r
-            /// simple progress dialog\r
-            ProgressDialog working_dialog = new ProgressDialog(this);\r
-            working_dialog.setMessage(getResources().getString(R.string.auth_trying_to_login));\r
-            working_dialog.setIndeterminate(true);\r
-            working_dialog.setCancelable(true);\r
-            working_dialog\r
-            .setOnCancelListener(new DialogInterface.OnCancelListener() {\r
-                @Override\r
-                public void onCancel(DialogInterface dialog) {\r
-                    /// TODO study if this is enough\r
-                    Log_OC.i(TAG, "Login canceled");\r
-                    if (mOperationThread != null) {\r
-                        mOperationThread.interrupt();\r
-                        finish();\r
-                    }\r
-                }\r
-            });\r
-            dialog = working_dialog;\r
-            break;\r
-        }\r
-        case DIALOG_OAUTH2_LOGIN_PROGRESS: {\r
-            ProgressDialog working_dialog = new ProgressDialog(this);\r
-            working_dialog.setMessage(String.format("Getting authorization")); \r
-            working_dialog.setIndeterminate(true);\r
-            working_dialog.setCancelable(true);\r
-            working_dialog\r
-            .setOnCancelListener(new DialogInterface.OnCancelListener() {\r
-                @Override\r
-                public void onCancel(DialogInterface dialog) {\r
-                    Log_OC.i(TAG, "Login canceled");\r
-                    finish();\r
-                }\r
-            });\r
-            dialog = working_dialog;\r
-            break;\r
-        }\r
-        case DIALOG_SSL_VALIDATOR: {\r
-            /// TODO start to use new dialog interface, at least for this (it is a FragmentDialog already)\r
-            dialog = SslValidatorDialog.newInstance(this, mLastSslUntrustedServerResult, this);\r
-            break;\r
-        }\r
-        case DIALOG_CERT_NOT_SAVED: {\r
-            AlertDialog.Builder 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
-            Log_OC.e(TAG, "Incorrect dialog called with id = " + id);\r
-        }\r
-        return dialog;\r
-    }\r
-\r
-\r
-    /**\r
-     * Starts and activity to open the 'new account' page in the ownCloud web site\r
-     * \r
-     * @param view      'Account register' button\r
-     */\r
-    public void onRegisterClick(View view) {\r
-        Intent register = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_account_register)));\r
-        setResult(RESULT_CANCELED);\r
-        startActivity(register);\r
-    }\r
-\r
-\r
-    /**\r
-     * Updates the content and visibility state of the icon and text associated\r
-     * to the last check on the ownCloud server.\r
-     */\r
-    private void showServerStatus() {\r
-        TextView tv = (TextView) findViewById(R.id.server_status_text);\r
-\r
-        if (mServerStatusIcon == 0 && mServerStatusText == 0) {\r
-            tv.setVisibility(View.INVISIBLE);\r
-\r
-        } else {\r
-            tv.setText(mServerStatusText);\r
-            tv.setCompoundDrawablesWithIntrinsicBounds(mServerStatusIcon, 0, 0, 0);\r
-            tv.setVisibility(View.VISIBLE);\r
-        }\r
-\r
-    }\r
-\r
-\r
-    /**\r
-     * Updates the content and visibility state of the icon and text associated\r
-     * to the interactions with the OAuth authorization server.\r
-     */\r
-    private void showAuthStatus() {\r
-        if (mAuthStatusIcon == 0 && mAuthStatusText == 0) {\r
-            mAuthStatusLayout.setVisibility(View.INVISIBLE);\r
-\r
-        } else {\r
-            mAuthStatusLayout.setText(mAuthStatusText);\r
-            mAuthStatusLayout.setCompoundDrawablesWithIntrinsicBounds(mAuthStatusIcon, 0, 0, 0);\r
-            mAuthStatusLayout.setVisibility(View.VISIBLE);\r
-        }\r
-    }     \r
-\r
-\r
-    private void showRefreshButton() {\r
-        mRefreshButton.setVisibility(View.VISIBLE);\r
-    }\r
-\r
-    private void hideRefreshButton() {\r
-        mRefreshButton.setVisibility(View.GONE);\r
-    }\r
-\r
-    /**\r
-     * Called when the refresh button in the input field for ownCloud host is clicked.\r
-     * \r
-     * Performs a new check on the URL in the input field.\r
-     * \r
-     * @param view      Refresh 'button'\r
-     */\r
-    public void onRefreshClick(View view) {\r
-        checkOcServer();\r
-    }\r
-    \r
-    \r
-    /**\r
-     * Called when the eye icon in the password field is clicked.\r
-     * \r
-     * Toggles the visibility of the password in the field. \r
-     */\r
-    public void onViewPasswordClick() {\r
-        int selectionStart = mPasswordInput.getSelectionStart();\r
-        int selectionEnd = mPasswordInput.getSelectionEnd();\r
-        if (isPasswordVisible()) {\r
-            hidePassword();\r
-        } else {\r
-            showPassword();\r
-        }\r
-        mPasswordInput.setSelection(selectionStart, selectionEnd);\r
-    }    \r
-\r
-\r
-    /**\r
-     * Called when the checkbox for OAuth authorization is clicked.\r
-     * \r
-     * Hides or shows the input fields for user & password. \r
-     * \r
-     * @param view      'View password' 'button'\r
-     */\r
-    public void onCheckClick(View view) {\r
-        CheckBox oAuth2Check = (CheckBox)view;\r
-        if (oAuth2Check.isChecked()) {\r
-            mAuthTokenType = MainApp.getAuthTokenTypeAccessToken();\r
-        } else {\r
-            mAuthTokenType = MainApp.getAuthTokenTypePass();\r
-        }\r
-        adaptViewAccordingToAuthenticationMethod();\r
-    }\r
-\r
-    \r
-    /**\r
-     * Changes the visibility of input elements depending on\r
-     * the current authorization method.\r
-     */\r
-    private void adaptViewAccordingToAuthenticationMethod () {\r
-        if (MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType)) {\r
-            // OAuth 2 authorization\r
-            mOAuthAuthEndpointText.setVisibility(View.VISIBLE);\r
-            mOAuthTokenEndpointText.setVisibility(View.VISIBLE);\r
-            mUsernameInput.setVisibility(View.GONE);\r
-            mPasswordInput.setVisibility(View.GONE);\r
-            \r
-        } else if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) {\r
-            // SAML-based web Single Sign On\r
-            mOAuthAuthEndpointText.setVisibility(View.GONE);\r
-            mOAuthTokenEndpointText.setVisibility(View.GONE);\r
-            mUsernameInput.setVisibility(View.GONE);\r
-            mPasswordInput.setVisibility(View.GONE);\r
-        } else {\r
-            // basic HTTP authorization\r
-            mOAuthAuthEndpointText.setVisibility(View.GONE);\r
-            mOAuthTokenEndpointText.setVisibility(View.GONE);\r
-            mUsernameInput.setVisibility(View.VISIBLE);\r
-            mPasswordInput.setVisibility(View.VISIBLE);\r
-        }\r
-    }\r
-    \r
-    /**\r
-     * Called from SslValidatorDialog when a new server certificate was correctly saved.\r
-     */\r
-    public void onSavedCertificate() {\r
-        checkOcServer();\r
-    }\r
-\r
-    /**\r
-     * Called from SslValidatorDialog when a new server certificate could not be saved \r
-     * when the user requested it.\r
-     */\r
-    @Override\r
-    public void onFailedSavingCertificate() {\r
-        showDialog(DIALOG_CERT_NOT_SAVED);\r
-    }\r
-\r
-\r
-    /**\r
-     *  Called when the 'action' button in an IME is pressed ('enter' in software keyboard).\r
-     * \r
-     *  Used to trigger the authentication check when the user presses 'enter' after writing the password, \r
-     *  or to throw the server test when the only field on screen is the URL input field.\r
-     */\r
-    @Override\r
-    public boolean onEditorAction(TextView inputField, int actionId, KeyEvent event) {\r
-        if (actionId == EditorInfo.IME_ACTION_DONE && inputField != null && inputField.equals(mPasswordInput)) {\r
-            if (mOkButton.isEnabled()) {\r
-                mOkButton.performClick();\r
-            }\r
-            \r
-        } else if (actionId == EditorInfo.IME_ACTION_NEXT && inputField != null && inputField.equals(mHostUrlInput)) {\r
-            if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) {\r
-                checkOcServer();\r
-            }\r
-        }\r
-        return false;   // always return false to grant that the software keyboard is hidden anyway\r
-    }\r
-\r
-\r
-    private abstract static class RightDrawableOnTouchListener implements OnTouchListener  {\r
-\r
-        private int fuzz = 75;\r
-        \r
-        /**\r
-         * {@inheritDoc}\r
-         */\r
-        @Override\r
-        public boolean onTouch(View view, MotionEvent event) {\r
-            Drawable rightDrawable = null;\r
-            if (view instanceof TextView) {\r
-                Drawable[] drawables = ((TextView)view).getCompoundDrawables();\r
-                if (drawables.length > 2) {\r
-                    rightDrawable = drawables[2];\r
-                }\r
-            }\r
-            if (rightDrawable != null) {\r
-                final int x = (int) event.getX();\r
-                final int y = (int) event.getY();\r
-                final Rect bounds = rightDrawable.getBounds();\r
-                if (x >= (view.getRight() - bounds.width() - fuzz) && x <= (view.getRight() - view.getPaddingRight() + fuzz)\r
-                    && y >= (view.getPaddingTop() - fuzz) && y <= (view.getHeight() - view.getPaddingBottom()) + fuzz) {\r
-                    \r
-                    return onDrawableTouch(event);\r
-                }\r
-            }\r
-            return false;\r
-        }\r
-\r
-        public abstract boolean onDrawableTouch(final MotionEvent event);\r
-    }\r
-\r
-\r
-    public void onSamlDialogSuccess(String sessionCookie){\r
-        mAuthToken = sessionCookie;\r
-        \r
-        if (sessionCookie != null && sessionCookie.length() > 0) {\r
-            mAuthToken = sessionCookie;\r
-            boolean success = false;\r
-            if (mAction == ACTION_CREATE) {\r
-                success = createAccount();\r
-        \r
-            } else {\r
-                success = updateToken();\r
-            }\r
-            if (success) {\r
-                finish();\r
-            }\r
-        }\r
-\r
-            \r
-    }\r
-\r
-\r
-    @Override\r
-    public void onSsoFinished(String sessionCookies) {\r
-        //Toast.makeText(this, "got cookies: " + sessionCookie, Toast.LENGTH_LONG).show();\r
-\r
-        if (sessionCookies != null && sessionCookies.length() > 0) {\r
-            Log_OC.d(TAG, "Successful SSO - time to save the account");\r
-            onSamlDialogSuccess(sessionCookies);\r
-            Fragment fd = getSupportFragmentManager().findFragmentByTag(TAG_SAML_DIALOG);\r
-            if (fd != null && fd instanceof SherlockDialogFragment) {\r
-                Dialog d = ((SherlockDialogFragment)fd).getDialog();\r
-                if (d != null && d.isShowing()) {\r
-                    d.dismiss();\r
-                }\r
-            }\r
-\r
-        } else { \r
-            // TODO - show fail\r
-            Log_OC.d(TAG, "SSO failed");\r
-        }\r
-    \r
-    }\r
-    \r
-    /** Show auth_message \r
-     * \r
-     * @param message\r
-     */\r
-    private void showAuthMessage(String message) {\r
-       mAuthMessage.setVisibility(View.VISIBLE);\r
-       mAuthMessage.setText(message);\r
-    }\r
-    \r
-    private void hideAuthMessage() {\r
-        mAuthMessage.setVisibility(View.GONE);\r
-    }\r
-\r
-    private void syncAccount(){\r
-        /// immediately request for the synchronization of the new account\r
-        Bundle bundle = new Bundle();\r
-        bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);\r
-        ContentResolver.requestSync(mAccount, MainApp.getAuthTokenType(), bundle);\r
-    }\r
-    \r
-    @Override\r
-    public boolean onTouchEvent(MotionEvent event) {\r
-        if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType) &&\r
-                mHostUrlInput.hasFocus() && event.getAction() == MotionEvent.ACTION_DOWN) {\r
-            checkOcServer();\r
-        }\r
-        return super.onTouchEvent(event);\r
-    }\r
-}\r
diff --git a/src/de/mobilcom/debitel/cloud/android/authentication/OAuth2Constants.java b/src/de/mobilcom/debitel/cloud/android/authentication/OAuth2Constants.java
deleted file mode 100644 (file)
index bf81270..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.authentication;
-
-/** 
- * Constant values for OAuth 2 protocol.
- * 
- * Includes required and optional parameter NAMES used in the 'authorization code' grant type.
- *  
- * @author David A. Velasco
- */
-
-public class OAuth2Constants {
-    
-    /// Parameters to send to the Authorization Endpoint
-    public static final String KEY_RESPONSE_TYPE = "response_type";
-    public static final String KEY_REDIRECT_URI = "redirect_uri";
-    public static final String KEY_CLIENT_ID = "client_id";
-    public static final String KEY_SCOPE = "scope";
-    public static final String KEY_STATE = "state"; 
-    
-    /// Additional parameters to send to the Token Endpoint
-    public static final String KEY_GRANT_TYPE = "grant_type";
-    public static final String KEY_CODE = "code";
-    
-    /// Parameters received in an OK response from the Token Endpoint 
-    public static final String KEY_ACCESS_TOKEN = "access_token";
-    public static final String KEY_TOKEN_TYPE = "token_type";
-    public static final String KEY_EXPIRES_IN = "expires_in";
-    public static final String KEY_REFRESH_TOKEN = "refresh_token";
-    
-    /// Parameters in an ERROR response
-    public static final String KEY_ERROR = "error";
-    public static final String KEY_ERROR_DESCRIPTION = "error_description";
-    public static final String KEY_ERROR_URI = "error_uri";
-    public static final String VALUE_ERROR_ACCESS_DENIED = "access_denied";
-    
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/authentication/SsoWebViewClient.java b/src/de/mobilcom/debitel/cloud/android/authentication/SsoWebViewClient.java
deleted file mode 100644 (file)
index 550fa6d..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.authentication;
-
-import java.lang.ref.WeakReference;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-
-import android.graphics.Bitmap;
-import android.os.Handler;
-import android.os.Message;
-import android.view.View;
-import android.webkit.CookieManager;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-
-
-/**
- * Custom {@link WebViewClient} client aimed to catch the end of a single-sign-on process 
- * running in the {@link WebView} that is attached to.
- * 
- * Assumes that the single-sign-on is kept thanks to a cookie set at the end of the
- * authentication process.
- *   
- * @author David A. Velasco
- */
-public class SsoWebViewClient extends WebViewClient {
-        
-    private static final String TAG = SsoWebViewClient.class.getSimpleName();
-    
-    public interface SsoWebViewClientListener {
-        public void onSsoFinished(String sessionCookie);
-    }
-    
-    private Handler mListenerHandler;
-    private WeakReference<SsoWebViewClientListener> mListenerRef;
-    private String mTargetUrl;
-    private String mLastReloadedUrlAtError;
-    
-    public SsoWebViewClient (Handler listenerHandler, SsoWebViewClientListener listener) {
-        mListenerHandler = listenerHandler;
-        mListenerRef = new WeakReference<SsoWebViewClient.SsoWebViewClientListener>(listener);
-        mTargetUrl = "fake://url.to.be.set";
-        mLastReloadedUrlAtError = null;
-    }
-    
-    public String getTargetUrl() {
-        return mTargetUrl;
-    }
-    
-    public void setTargetUrl(String targetUrl) {
-        mTargetUrl = targetUrl;
-    }
-
-    @Override
-    public void onPageStarted (WebView view, String url, Bitmap favicon) {
-        Log_OC.d(TAG, "onPageStarted : " + url);
-        super.onPageStarted(view, url, favicon);
-    }
-    
-    @Override
-    public void onFormResubmission (WebView view, Message dontResend, Message resend) {
-        Log_OC.d(TAG, "onFormResubMission ");
-
-        // necessary to grant reload of last page when device orientation is changed after sending a form
-        resend.sendToTarget();
-    }
-
-    @Override
-    public boolean shouldOverrideUrlLoading(WebView view, String url) {
-        return false;
-    }
-    
-    @Override
-    public void onReceivedError (WebView view, int errorCode, String description, String failingUrl) {
-        Log_OC.e(TAG, "onReceivedError : " + failingUrl + ", code " + errorCode + ", description: " + description);
-        if (!failingUrl.equals(mLastReloadedUrlAtError)) {
-            view.reload();
-            mLastReloadedUrlAtError = failingUrl;
-        } else {
-            mLastReloadedUrlAtError = null;
-            super.onReceivedError(view, errorCode, description, failingUrl);
-        }
-    }
-    
-    @Override
-    public void onPageFinished (WebView view, String url) {
-        Log_OC.d(TAG, "onPageFinished : " + url);
-        mLastReloadedUrlAtError = null;
-        if (url.startsWith(mTargetUrl)) {
-            view.setVisibility(View.GONE);
-            CookieManager cookieManager = CookieManager.getInstance();
-            final String cookies = cookieManager.getCookie(url);
-            //Log_OC.d(TAG, "Cookies: " + cookies);
-            if (mListenerHandler != null && mListenerRef != null) {
-                // this is good idea because onPageFinished is not running in the UI thread
-                mListenerHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        SsoWebViewClientListener listener = mListenerRef.get();
-                        if (listener != null) {
-                            listener.onSsoFinished(cookies);
-                        }
-                    }
-                });
-            }
-        }
-
-    }
-    
-    /*
-    @Override
-    public void doUpdateVisitedHistory (WebView view, String url, boolean isReload) {
-        Log_OC.d(TAG, "doUpdateVisitedHistory : " + url);
-    }
-    
-    @Override
-    public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) {
-        Log_OC.d(TAG, "onReceivedSslError : " + error);
-    }
-    
-    @Override
-    public void onReceivedHttpAuthRequest (WebView view, HttpAuthHandler handler, String host, String realm) {
-        Log_OC.d(TAG, "onReceivedHttpAuthRequest : " + host);
-    }
-
-    @Override
-    public WebResourceResponse shouldInterceptRequest (WebView view, String url) {
-        Log_OC.d(TAG, "shouldInterceptRequest : " + url);
-        return null;
-    }
-    
-    @Override
-    public void onLoadResource (WebView view, String url) {
-        Log_OC.d(TAG, "onLoadResource : " + url);   
-    }
-    
-    @Override
-    public void onReceivedLoginRequest (WebView view, String realm, String account, String args) {
-        Log_OC.d(TAG, "onReceivedLoginRequest : " + realm + ", " + account + ", " + args);
-    }
-    
-    @Override
-    public void onScaleChanged (WebView view, float oldScale, float newScale) {
-        Log_OC.d(TAG, "onScaleChanged : " + oldScale + " -> " + newScale);
-        super.onScaleChanged(view, oldScale, newScale);
-    }
-
-    @Override
-    public void onUnhandledKeyEvent (WebView view, KeyEvent event) {
-        Log_OC.d(TAG, "onUnhandledKeyEvent : " + event);
-    }
-    
-    @Override
-    public boolean shouldOverrideKeyEvent (WebView view, KeyEvent event) {
-        Log_OC.d(TAG, "shouldOverrideKeyEvent : " + event);
-        return false;
-    }
-    */
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/datamodel/DataStorageManager.java b/src/de/mobilcom/debitel/cloud/android/datamodel/DataStorageManager.java
deleted file mode 100644 (file)
index bbe06fc..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.datamodel;
-
-import java.util.List;
-import java.util.Vector;
-
-public interface DataStorageManager {
-
-    public static final int ROOT_PARENT_ID = 0;
-    
-    public OCFile getFileByPath(String path);
-
-    public OCFile getFileById(long id);
-
-    public boolean fileExists(String path);
-
-    public boolean fileExists(long id);
-
-    public boolean saveFile(OCFile file);
-
-    public void saveFiles(List<OCFile> files);
-
-    public Vector<OCFile> getDirectoryContent(OCFile f);
-    
-    public void removeFile(OCFile file, boolean removeLocalCopy);
-    
-    public void removeDirectory(OCFile dir, boolean removeDBData, boolean removeLocalContent);
-
-    public void moveDirectory(OCFile dir, String newPath);
-
-    public Vector<OCFile> getDirectoryImages(OCFile mParentFolder);
-    
-    public void calculateFolderSize(long id);
-    
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/datamodel/FileDataStorageManager.java b/src/de/mobilcom/debitel/cloud/android/datamodel/FileDataStorageManager.java
deleted file mode 100644 (file)
index 32aa09c..0000000
+++ /dev/null
@@ -1,689 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.datamodel;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Vector;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.db.ProviderMeta.ProviderTableMeta;
-import de.mobilcom.debitel.cloud.android.utils.FileStorageUtils;
-
-import android.accounts.Account;
-import android.content.ContentProviderClient;
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderResult;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.OperationApplicationException;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.RemoteException;
-
-public class FileDataStorageManager implements DataStorageManager {
-
-    private ContentResolver mContentResolver;
-    private ContentProviderClient mContentProvider;
-    private Account mAccount;
-
-    private static String TAG = "FileDataStorageManager";
-
-    public FileDataStorageManager(Account account, ContentResolver cr) {
-        mContentProvider = null;
-        mContentResolver = cr;
-        mAccount = account;
-    }
-
-    public FileDataStorageManager(Account account, ContentProviderClient cp) {
-        mContentProvider = cp;
-        mContentResolver = null;
-        mAccount = account;
-    }
-
-    @Override
-    public OCFile getFileByPath(String path) {
-        Cursor c = getCursorForValue(ProviderTableMeta.FILE_PATH, path);
-        OCFile file = null;
-        if (c.moveToFirst()) {
-            file = createFileInstance(c);
-        }
-        c.close();
-        if (file == null && OCFile.PATH_SEPARATOR.equals(path)) {
-            return createRootDir(); // root should always exist
-        }
-        return file;
-    }
-
-
-    private OCFile createRootDir() {
-        OCFile file = new OCFile(OCFile.PATH_SEPARATOR);
-        file.setMimetype("DIR");
-        file.setParentId(DataStorageManager.ROOT_PARENT_ID);
-        saveFile(file);
-        return file;
-    }
-
-    @Override
-    public OCFile getFileById(long id) {
-        Cursor c = getCursorForValue(ProviderTableMeta._ID, String.valueOf(id));
-        OCFile file = null;
-        if (c.moveToFirst()) {
-            file = createFileInstance(c);
-        }
-        c.close();
-        return file;
-    }
-
-    public OCFile getFileByLocalPath(String path) {
-        Cursor c = getCursorForValue(ProviderTableMeta.FILE_STORAGE_PATH, path);
-        OCFile file = null;
-        if (c.moveToFirst()) {
-            file = createFileInstance(c);
-        }
-        c.close();
-        return file;
-    }
-
-    @Override
-    public boolean fileExists(long id) {
-        return fileExists(ProviderTableMeta._ID, String.valueOf(id));
-    }
-
-    @Override
-    public boolean fileExists(String path) {
-        return fileExists(ProviderTableMeta.FILE_PATH, path);
-    }
-
-    @Override
-    public boolean saveFile(OCFile file) {
-        boolean overriden = false;
-        ContentValues cv = new ContentValues();
-        cv.put(ProviderTableMeta.FILE_MODIFIED, file.getModificationTimestamp());
-        cv.put(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, file.getModificationTimestampAtLastSyncForData());
-        cv.put(ProviderTableMeta.FILE_CREATION, file.getCreationTimestamp());
-        cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
-        cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
-        cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
-        if (file.getParentId() != 0)
-            cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
-        cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
-        if (!file.isDirectory())
-            cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
-        cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
-        cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
-        cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
-        cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.keepInSync() ? 1 : 0);
-
-        boolean sameRemotePath = fileExists(file.getRemotePath());
-        boolean changesSizeOfAncestors = false;
-        if (sameRemotePath ||
-                fileExists(file.getFileId())        ) {           // for renamed files; no more delete and create
-
-            OCFile oldFile = null;
-            if (sameRemotePath) {
-                oldFile = getFileByPath(file.getRemotePath());
-                file.setFileId(oldFile.getFileId());
-            } else {
-                oldFile = getFileById(file.getFileId());
-            }
-            changesSizeOfAncestors = (oldFile.getFileLength() != file.getFileLength());
-
-            overriden = true;
-            if (getContentResolver() != null) {
-                getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv,
-                        ProviderTableMeta._ID + "=?",
-                        new String[] { String.valueOf(file.getFileId()) });
-            } else {
-                try {
-                    getContentProvider().update(ProviderTableMeta.CONTENT_URI,
-                            cv, ProviderTableMeta._ID + "=?",
-                            new String[] { String.valueOf(file.getFileId()) });
-                } catch (RemoteException e) {
-                    Log_OC.e(TAG,
-                            "Fail to insert insert file to database "
-                                    + e.getMessage());
-                }
-            }
-        } else {
-            changesSizeOfAncestors = true;
-            Uri result_uri = null;
-            if (getContentResolver() != null) {
-                result_uri = getContentResolver().insert(
-                        ProviderTableMeta.CONTENT_URI_FILE, cv);
-            } else {
-                try {
-                    result_uri = getContentProvider().insert(
-                            ProviderTableMeta.CONTENT_URI_FILE, cv);
-                } catch (RemoteException e) {
-                    Log_OC.e(TAG,
-                            "Fail to insert insert file to database "
-                                    + e.getMessage());
-                }
-            }
-            if (result_uri != null) {
-                long new_id = Long.parseLong(result_uri.getPathSegments()
-                        .get(1));
-                file.setFileId(new_id);
-            }            
-        }
-
-        if (file.isDirectory()) {
-            calculateFolderSize(file.getFileId());
-            if (file.needsUpdatingWhileSaving()) {
-                for (OCFile f : getDirectoryContent(file))
-                    saveFile(f);
-            }
-        }
-        
-        if (changesSizeOfAncestors || file.isDirectory()) {
-            updateSizesToTheRoot(file.getParentId());
-        }
-        
-        return overriden;
-    }
-
-
-    @Override
-    public void saveFiles(List<OCFile> files) {
-
-        Iterator<OCFile> filesIt = files.iterator();
-        ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(files.size());
-        OCFile file = null;
-
-        // prepare operations to perform
-        while (filesIt.hasNext()) {
-            file = filesIt.next();
-            ContentValues cv = new ContentValues();
-            cv.put(ProviderTableMeta.FILE_MODIFIED, file.getModificationTimestamp());
-            cv.put(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, file.getModificationTimestampAtLastSyncForData());
-            cv.put(ProviderTableMeta.FILE_CREATION, file.getCreationTimestamp());
-            cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
-            cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
-            cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
-            if (file.getParentId() != 0)
-                cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
-            cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
-            if (!file.isDirectory())
-                cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
-            cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
-            cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
-            cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
-            cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.keepInSync() ? 1 : 0);
-
-            if (fileExists(file.getRemotePath())) {
-                OCFile oldFile = getFileByPath(file.getRemotePath());
-                file.setFileId(oldFile.getFileId());
-               
-                if (file.isDirectory()) {
-                    cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, oldFile.getFileLength());
-                    file.setFileLength(oldFile.getFileLength());
-                }
-                
-                operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
-                        withValues(cv).
-                        withSelection(  ProviderTableMeta._ID + "=?", 
-                                new String[] { String.valueOf(file.getFileId()) })
-                                .build());
-
-            } else if (fileExists(file.getFileId())) {
-                OCFile oldFile = getFileById(file.getFileId());
-                if (file.getStoragePath() == null && oldFile.getStoragePath() != null)
-                    file.setStoragePath(oldFile.getStoragePath());
-                
-                if (!file.isDirectory())
-                    cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
-                else {
-                    cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, oldFile.getFileLength());
-                    file.setFileLength(oldFile.getFileLength());
-                }
-                
-                operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
-                        withValues(cv).
-                        withSelection(  ProviderTableMeta._ID + "=?", 
-                                new String[] { String.valueOf(file.getFileId()) })
-                                .build());
-
-            } else {
-                operations.add(ContentProviderOperation.newInsert(ProviderTableMeta.CONTENT_URI).withValues(cv).build());
-            }
-        }
-
-        // apply operations in batch
-        ContentProviderResult[] results = null;
-        try {
-            if (getContentResolver() != null) {
-                results = getContentResolver().applyBatch(MainApp.getAuthority(), operations);
-
-            } else {
-                results = getContentProvider().applyBatch(operations);
-            }
-
-        } catch (OperationApplicationException e) {
-            Log_OC.e(TAG, "Fail to update/insert list of files to database " + e.getMessage());
-
-        } catch (RemoteException e) {
-            Log_OC.e(TAG, "Fail to update/insert list of files to database " + e.getMessage());
-        }
-
-        // update new id in file objects for insertions
-        if (results != null) {
-            long newId;
-            for (int i=0; i<results.length; i++) {
-                if (results[i].uri != null) {
-                    newId = Long.parseLong(results[i].uri.getPathSegments().get(1));
-                    files.get(i).setFileId(newId);
-                    //Log_OC.v(TAG, "Found and added id in insertion for " + files.get(i).getRemotePath());
-                }
-            }
-        }
-
-        for (OCFile aFile : files) {
-            if (aFile.isDirectory() && aFile.needsUpdatingWhileSaving())
-                saveFiles(getDirectoryContent(aFile));
-        }
-
-    }
-
-    public void setAccount(Account account) {
-        mAccount = account;
-    }
-
-    public Account getAccount() {
-        return mAccount;
-    }
-
-    public void setContentResolver(ContentResolver cr) {
-        mContentResolver = cr;
-    }
-
-    public ContentResolver getContentResolver() {
-        return mContentResolver;
-    }
-
-    public void setContentProvider(ContentProviderClient cp) {
-        mContentProvider = cp;
-    }
-
-    public ContentProviderClient getContentProvider() {
-        return mContentProvider;
-    }
-    
-    @Override
-    public Vector<OCFile> getDirectoryContent(OCFile f) {
-        if (f != null && f.isDirectory() && f.getFileId() != -1) {
-            return getDirectoryContent(f.getFileId());
-
-        } else {
-            return new Vector<OCFile>();
-        }
-    }
-
-    private Vector<OCFile> getDirectoryContent(long parentId) {
-
-        Vector<OCFile> ret = new Vector<OCFile>();
-
-        Uri req_uri = Uri.withAppendedPath(
-                ProviderTableMeta.CONTENT_URI_DIR,
-                String.valueOf(parentId));
-        Cursor c = null;
-
-        if (getContentProvider() != null) {
-            try {
-                c = getContentProvider().query(req_uri, null, 
-                        ProviderTableMeta.FILE_PARENT + "=?" ,
-                        new String[] { String.valueOf(parentId)}, null);
-            } catch (RemoteException e) {
-                Log_OC.e(TAG, e.getMessage());
-                return ret;
-            }
-        } else {
-            c = getContentResolver().query(req_uri, null, 
-                    ProviderTableMeta.FILE_PARENT + "=?" ,
-                    new String[] { String.valueOf(parentId)}, null);
-        }
-
-        if (c.moveToFirst()) {
-            do {
-                OCFile child = createFileInstance(c);
-                ret.add(child);
-            } while (c.moveToNext());
-        }
-
-        c.close();
-
-        Collections.sort(ret);
-
-        return ret;
-    }
-    
-    
-
-    private boolean fileExists(String cmp_key, String value) {
-        Cursor c;
-        if (getContentResolver() != null) {
-            c = getContentResolver()
-                    .query(ProviderTableMeta.CONTENT_URI,
-                            null,
-                            cmp_key + "=? AND "
-                                    + ProviderTableMeta.FILE_ACCOUNT_OWNER
-                                    + "=?",
-                                    new String[] { value, mAccount.name }, null);
-        } else {
-            try {
-                c = getContentProvider().query(
-                        ProviderTableMeta.CONTENT_URI,
-                        null,
-                        cmp_key + "=? AND "
-                                + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?",
-                                new String[] { value, mAccount.name }, null);
-            } catch (RemoteException e) {
-                Log_OC.e(TAG,
-                        "Couldn't determine file existance, assuming non existance: "
-                                + e.getMessage());
-                return false;
-            }
-        }
-        boolean retval = c.moveToFirst();
-        c.close();
-        return retval;
-    }
-
-    private Cursor getCursorForValue(String key, String value) {
-        Cursor c = null;
-        if (getContentResolver() != null) {
-            c = getContentResolver()
-                    .query(ProviderTableMeta.CONTENT_URI,
-                            null,
-                            key + "=? AND "
-                                    + ProviderTableMeta.FILE_ACCOUNT_OWNER
-                                    + "=?",
-                                    new String[] { value, mAccount.name }, null);
-        } else {
-            try {
-                c = getContentProvider().query(
-                        ProviderTableMeta.CONTENT_URI,
-                        null,
-                        key + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER
-                        + "=?", new String[] { value, mAccount.name },
-                        null);
-            } catch (RemoteException e) {
-                Log_OC.e(TAG, "Could not get file details: " + e.getMessage());
-                c = null;
-            }
-        }
-        return c;
-    }
-
-    private OCFile createFileInstance(Cursor c) {
-        OCFile file = null;
-        if (c != null) {
-            file = new OCFile(c.getString(c
-                    .getColumnIndex(ProviderTableMeta.FILE_PATH)));
-            file.setFileId(c.getLong(c.getColumnIndex(ProviderTableMeta._ID)));
-            file.setParentId(c.getLong(c
-                    .getColumnIndex(ProviderTableMeta.FILE_PARENT)));
-            file.setMimetype(c.getString(c
-                    .getColumnIndex(ProviderTableMeta.FILE_CONTENT_TYPE)));
-            if (!file.isDirectory()) {
-                file.setStoragePath(c.getString(c
-                        .getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH)));
-                if (file.getStoragePath() == null) {
-                    // try to find existing file and bind it with current account; - with the current update of SynchronizeFolderOperation, this won't be necessary anymore after a full synchronization of the account
-                    File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
-                    if (f.exists()) {
-                        file.setStoragePath(f.getAbsolutePath());
-                        file.setLastSyncDateForData(f.lastModified());
-                    }
-                }
-            }
-            file.setFileLength(c.getLong(c
-                    .getColumnIndex(ProviderTableMeta.FILE_CONTENT_LENGTH)));
-            file.setCreationTimestamp(c.getLong(c
-                    .getColumnIndex(ProviderTableMeta.FILE_CREATION)));
-            file.setModificationTimestamp(c.getLong(c
-                    .getColumnIndex(ProviderTableMeta.FILE_MODIFIED)));
-            file.setModificationTimestampAtLastSyncForData(c.getLong(c
-                    .getColumnIndex(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA)));
-            file.setLastSyncDateForProperties(c.getLong(c
-                    .getColumnIndex(ProviderTableMeta.FILE_LAST_SYNC_DATE)));
-            file.setLastSyncDateForData(c.getLong(c.
-                    getColumnIndex(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA)));
-            file.setKeepInSync(c.getInt(
-                    c.getColumnIndex(ProviderTableMeta.FILE_KEEP_IN_SYNC)) == 1 ? true : false);
-        }
-        return file;
-    }
-
-    @Override
-    public void removeFile(OCFile file, boolean removeLocalCopy) {
-        Uri file_uri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, ""+file.getFileId());
-        if (getContentProvider() != null) {
-            try {
-                getContentProvider().delete(file_uri,
-                        ProviderTableMeta.FILE_ACCOUNT_OWNER+"=?",
-                        new String[]{mAccount.name});
-            } catch (RemoteException e) {
-                e.printStackTrace();
-            }
-        } else {
-            getContentResolver().delete(file_uri,
-                    ProviderTableMeta.FILE_ACCOUNT_OWNER+"=?",
-                    new String[]{mAccount.name});
-        }
-        if (file.isDown() && removeLocalCopy) {
-            new File(file.getStoragePath()).delete();
-        }
-        if (file.isDirectory() && removeLocalCopy) {
-            File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
-            if (f.exists() && f.isDirectory() && (f.list() == null || f.list().length == 0)) {
-                f.delete();
-            }
-        }
-        
-        if (file.getFileLength() > 0) {
-            updateSizesToTheRoot(file.getParentId());
-        }
-    }
-
-    @Override
-    public void removeDirectory(OCFile dir, boolean removeDBData, boolean removeLocalContent) {
-        // TODO consider possible failures
-        if (dir != null && dir.isDirectory() && dir.getFileId() != -1) {
-            Vector<OCFile> children = getDirectoryContent(dir);
-            if (children.size() > 0) {
-                OCFile child = null;
-                for (int i=0; i<children.size(); i++) {
-                    child = children.get(i);
-                    if (child.isDirectory()) {
-                        removeDirectory(child, removeDBData, removeLocalContent);
-                    } else {
-                        if (removeDBData) {
-                            removeFile(child, removeLocalContent);
-                        } else if (removeLocalContent) {
-                            if (child.isDown()) {
-                                new File(child.getStoragePath()).delete();
-                            }
-                        }
-                    }
-                }
-            }
-            if (removeDBData) {
-                removeFile(dir, true);
-            }
-            
-            if (dir.getFileLength() > 0) {
-                updateSizesToTheRoot(dir.getParentId());
-            }
-        }
-    }
-
-
-    /**
-     * Updates database for a folder that was moved to a different location.
-     * 
-     * TODO explore better (faster) implementations
-     * TODO throw exceptions up !
-     */
-    @Override
-    public void moveDirectory(OCFile dir, String newPath) {
-        // TODO check newPath
-
-        if (dir != null && dir.isDirectory() && dir.fileExists() && !dir.getFileName().equals(OCFile.PATH_SEPARATOR)) {
-            /// 1. get all the descendants of 'dir' in a single QUERY (including 'dir')
-            Cursor c = null;
-            if (getContentProvider() != null) {
-                try {
-                    c = getContentProvider().query(ProviderTableMeta.CONTENT_URI, 
-                            null,
-                            ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + ProviderTableMeta.FILE_PATH + " LIKE ? ",
-                            new String[] { mAccount.name, dir.getRemotePath() + "%"  }, ProviderTableMeta.FILE_PATH + " ASC ");
-                } catch (RemoteException e) {
-                    Log_OC.e(TAG, e.getMessage());
-                }
-            } else {
-                c = getContentResolver().query(ProviderTableMeta.CONTENT_URI, 
-                        null,
-                        ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + ProviderTableMeta.FILE_PATH + " LIKE ? ",
-                        new String[] { mAccount.name, dir.getRemotePath() + "%"  }, ProviderTableMeta.FILE_PATH + " ASC ");
-            }
-
-            /// 2. prepare a batch of update operations to change all the descendants
-            ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(c.getCount());
-            int lengthOfOldPath = dir.getRemotePath().length();
-            String defaultSavePath = FileStorageUtils.getSavePath(mAccount.name);
-            int lengthOfOldStoragePath = defaultSavePath.length() + lengthOfOldPath;
-            if (c.moveToFirst()) {
-                do {
-                    ContentValues cv = new ContentValues(); // don't take the constructor out of the loop and clear the object
-                    OCFile child = createFileInstance(c);
-                    cv.put(ProviderTableMeta.FILE_PATH, newPath + child.getRemotePath().substring(lengthOfOldPath));
-                    if (child.getStoragePath() != null && child.getStoragePath().startsWith(defaultSavePath)) {
-                        cv.put(ProviderTableMeta.FILE_STORAGE_PATH, defaultSavePath + newPath + child.getStoragePath().substring(lengthOfOldStoragePath));
-                    }
-                    operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
-                            withValues(cv).
-                            withSelection(  ProviderTableMeta._ID + "=?", 
-                                    new String[] { String.valueOf(child.getFileId()) })
-                                    .build());
-                } while (c.moveToNext());
-            }
-            c.close();
-
-            /// 3. apply updates in batch
-            try {
-                if (getContentResolver() != null) {
-                    getContentResolver().applyBatch(MainApp.getAuthority(), operations);
-
-                } else {
-                    getContentProvider().applyBatch(operations);
-                }
-
-            } catch (OperationApplicationException e) {
-                Log_OC.e(TAG, "Fail to update descendants of " + dir.getFileId() + " in database", e);
-
-            } catch (RemoteException 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;
-    }
-
-    /**
-     * Calculate and save the folderSize on DB
-     * @param id
-     */
-    @Override
-    public void calculateFolderSize(long id) {
-        long folderSize = 0;
-        
-        Vector<OCFile> files = getDirectoryContent(id);
-        
-        for (OCFile f: files)
-        {
-            folderSize = folderSize + f.getFileLength();
-        }
-        
-        updateSize(id, folderSize);
-    }
-
-    /**
-     * Update the size value of an OCFile in DB
-     */
-    private int updateSize(long id, long size) {
-        ContentValues cv = new ContentValues();
-        cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, size);
-        int result = -1;
-        if (getContentResolver() != null) {
-             result = getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv, ProviderTableMeta._ID + "=?", 
-                     new String[] { String.valueOf(id) });
-        } else {
-            try {
-                result = getContentProvider().update(ProviderTableMeta.CONTENT_URI, cv, ProviderTableMeta._ID + "=?", 
-                        new String[] { String.valueOf(id) });
-            } catch (RemoteException e) {
-                Log_OC.e(TAG,"Fail to update size column into database " + e.getMessage());
-            }
-        }
-        return result;
-    }
-
-    /** 
-     * Update the size of a subtree of folder from a file to the root
-     * @param parentId: parent of the file
-     */
-    private void updateSizesToTheRoot(long parentId) {
-        
-        OCFile file; 
-
-        while (parentId != 0) {
-            
-            // Update the size of the parent
-            calculateFolderSize(parentId);
-            
-            // search the next parent
-            file = getFileById(parentId);            
-            parentId = file.getParentId();
-            
-        }              
-        
-    }
-    
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/datamodel/OCFile.java b/src/de/mobilcom/debitel/cloud/android/datamodel/OCFile.java
deleted file mode 100644 (file)
index 1865768..0000000
+++ /dev/null
@@ -1,485 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.datamodel;
-
-import java.io.File;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.webkit.MimeTypeMap;
-
-public class OCFile implements Parcelable, Comparable<OCFile> {
-
-    public static final Parcelable.Creator<OCFile> CREATOR = new Parcelable.Creator<OCFile>() {
-        @Override
-        public OCFile createFromParcel(Parcel source) {
-            return new OCFile(source);
-        }
-
-        @Override
-        public OCFile[] newArray(int size) {
-            return new OCFile[size];
-        }
-    };
-
-    public static final String PATH_SEPARATOR = "/";
-
-    private static final String TAG = OCFile.class.getSimpleName();
-    
-    private long mId;
-    private long mParentId;
-    private long mLength;
-    private long mCreationTimestamp;
-    private long mModifiedTimestamp;
-    private long mModifiedTimestampAtLastSyncForData;
-    private String mRemotePath;
-    private String mLocalPath;
-    private String mMimeType;
-    private boolean mNeedsUpdating;
-    private long mLastSyncDateForProperties;
-    private long mLastSyncDateForData;
-    private boolean mKeepInSync;
-
-    private String mEtag;
-
-    /**
-     * Create new {@link OCFile} with given path.
-     * 
-     * The path received must be URL-decoded. Path separator must be OCFile.PATH_SEPARATOR, and it must be the first character in 'path'.
-     * 
-     * @param path The remote path of the file.
-     */
-    public OCFile(String path) {
-        resetData();
-        mNeedsUpdating = false;
-        if (path == null || path.length() <= 0 || !path.startsWith(PATH_SEPARATOR)) {
-            throw new IllegalArgumentException("Trying to create a OCFile with a non valid remote path: " + path);
-        }
-        mRemotePath = path;
-    }
-
-    /**
-     * Reconstruct from parcel
-     * 
-     * @param source The source parcel
-     */
-    private OCFile(Parcel source) {
-        mId = source.readLong();
-        mParentId = source.readLong();
-        mLength = source.readLong();
-        mCreationTimestamp = source.readLong();
-        mModifiedTimestamp = source.readLong();
-        mModifiedTimestampAtLastSyncForData = source.readLong();
-        mRemotePath = source.readString();
-        mLocalPath = source.readString();
-        mMimeType = source.readString();
-        mNeedsUpdating = source.readInt() == 0;
-        mKeepInSync = source.readInt() == 1;
-        mLastSyncDateForProperties = source.readLong();
-        mLastSyncDateForData = source.readLong();
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeLong(mId);
-        dest.writeLong(mParentId);
-        dest.writeLong(mLength);
-        dest.writeLong(mCreationTimestamp);
-        dest.writeLong(mModifiedTimestamp);
-        dest.writeLong(mModifiedTimestampAtLastSyncForData);
-        dest.writeString(mRemotePath);
-        dest.writeString(mLocalPath);
-        dest.writeString(mMimeType);
-        dest.writeInt(mNeedsUpdating ? 1 : 0);
-        dest.writeInt(mKeepInSync ? 1 : 0);
-        dest.writeLong(mLastSyncDateForProperties);
-        dest.writeLong(mLastSyncDateForData);
-    }
-    
-    /**
-     * Gets the ID of the file
-     * 
-     * @return the file ID
-     */
-    public long getFileId() {
-        return mId;
-    }
-
-    /**
-     * Returns the remote path of the file on ownCloud
-     * 
-     * @return The remote path to the file
-     */
-    public String getRemotePath() {
-        return mRemotePath;
-    }
-
-    /**
-     * Can be used to check, whether or not this file exists in the database
-     * already
-     * 
-     * @return true, if the file exists in the database
-     */
-    public boolean fileExists() {
-        return mId != -1;
-    }
-
-    /**
-     * Use this to find out if this file is a Directory
-     * 
-     * @return true if it is a directory
-     */
-    public boolean isDirectory() {
-        return mMimeType != null && mMimeType.equals("DIR");
-    }
-
-    /**
-     * Use this to check if this file is available locally
-     * 
-     * @return true if it is
-     */
-    public boolean isDown() {
-        if (mLocalPath != null && mLocalPath.length() > 0) {
-            File file = new File(mLocalPath);
-            return (file.exists());
-        }
-        return false;
-    }
-    
-    /**
-     * The path, where the file is stored locally
-     * 
-     * @return The local path to the file
-     */
-    public String getStoragePath() {
-        return mLocalPath;
-    }
-
-    /**
-     * Can be used to set the path where the file is stored
-     * 
-     * @param storage_path to set
-     */
-    public void setStoragePath(String storage_path) {
-        mLocalPath = storage_path;
-    }
-
-    /**
-     * Get a UNIX timestamp of the file creation time
-     * 
-     * @return A UNIX timestamp of the time that file was created
-     */
-    public long getCreationTimestamp() {
-        return mCreationTimestamp;
-    }
-
-    /**
-     * Set a UNIX timestamp of the time the file was created
-     * 
-     * @param creation_timestamp to set
-     */
-    public void setCreationTimestamp(long creation_timestamp) {
-        mCreationTimestamp = creation_timestamp;
-    }
-
-    /**
-     * Get a UNIX timestamp of the file modification time.
-     *
-     * @return  A UNIX timestamp of the modification time, corresponding to the value returned by the server
-     *          in the last synchronization of the properties of this file. 
-     */
-    public long getModificationTimestamp() {
-        return mModifiedTimestamp;
-    }
-
-    /**
-     * Set a UNIX timestamp of the time the time the file was modified.
-     * 
-     * To update with the value returned by the server in every synchronization of the properties 
-     * of this file.
-     * 
-     * @param modification_timestamp to set
-     */
-    public void setModificationTimestamp(long modification_timestamp) {
-        mModifiedTimestamp = modification_timestamp;
-    }
-
-    
-    /**
-     * Get a UNIX timestamp of the file modification time.
-     *
-     * @return  A UNIX timestamp of the modification time, corresponding to the value returned by the server
-     *          in the last synchronization of THE CONTENTS of this file. 
-     */
-    public long getModificationTimestampAtLastSyncForData() {
-        return mModifiedTimestampAtLastSyncForData;
-    }
-
-    /**
-     * Set a UNIX timestamp of the time the time the file was modified.
-     * 
-     * To update with the value returned by the server in every synchronization of THE CONTENTS 
-     * of this file.
-     * 
-     * @param modification_timestamp to set
-     */
-    public void setModificationTimestampAtLastSyncForData(long modificationTimestamp) {
-        mModifiedTimestampAtLastSyncForData = modificationTimestamp;
-    }
-
-    
-    
-    /**
-     * Returns the filename and "/" for the root directory
-     * 
-     * @return The name of the file
-     */
-    public String getFileName() {
-        File f = new File(getRemotePath());
-        return f.getName().length() == 0 ? PATH_SEPARATOR : f.getName();
-    }
-    
-    /**
-     * Sets the name of the file
-     * 
-     * 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_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;
-            mRemotePath =  parent + name;
-            if (isDirectory()) {
-                mRemotePath += PATH_SEPARATOR;
-            }
-            Log_OC.d(TAG, "OCFile name changed to " + mRemotePath);
-        }
-    }
-
-    /**
-     * Can be used to get the Mimetype
-     * 
-     * @return the Mimetype as a String
-     */
-    public String getMimetype() {
-        return mMimeType;
-    }
-
-    /**
-     * Adds a file to this directory. If this file is not a directory, an
-     * exception gets thrown.
-     * 
-     * @param file to add
-     * @throws IllegalStateException if you try to add a something and this is
-     *             not a directory
-     */
-    public void addFile(OCFile file) throws IllegalStateException {
-        if (isDirectory()) {
-            file.mParentId = mId;
-            mNeedsUpdating = true;
-            return;
-        }
-        throw new IllegalStateException(
-                "This is not a directory where you can add stuff to!");
-    }
-
-    /**
-     * Used internally. Reset all file properties
-     */
-    private void resetData() {
-        mId = -1;
-        mRemotePath = null;
-        mParentId = 0;
-        mLocalPath = null;
-        mMimeType = null;
-        mLength = 0;
-        mCreationTimestamp = 0;
-        mModifiedTimestamp = 0;
-        mModifiedTimestampAtLastSyncForData = 0;
-        mLastSyncDateForProperties = 0;
-        mLastSyncDateForData = 0;
-        mKeepInSync = false;
-        mNeedsUpdating = false;
-    }
-
-    /**
-     * Sets the ID of the file
-     * 
-     * @param file_id to set
-     */
-    public void setFileId(long file_id) {
-        mId = file_id;
-    }
-
-    /**
-     * Sets the Mime-Type of the
-     * 
-     * @param mimetype to set
-     */
-    public void setMimetype(String mimetype) {
-        mMimeType = mimetype;
-    }
-
-    /**
-     * Sets the ID of the parent folder
-     * 
-     * @param parent_id to set
-     */
-    public void setParentId(long parent_id) {
-        mParentId = parent_id;
-    }
-
-    /**
-     * Sets the file size in bytes
-     * 
-     * @param file_len to set
-     */
-    public void setFileLength(long file_len) {
-        mLength = file_len;
-    }
-
-    /**
-     * Returns the size of the file in bytes
-     * 
-     * @return The filesize in bytes
-     */
-    public long getFileLength() {
-        return mLength;
-    }
-
-    /**
-     * Returns the ID of the parent Folder
-     * 
-     * @return The ID
-     */
-    public long getParentId() {
-        return mParentId;
-    }
-
-    /**
-     * Check, if this file needs updating
-     * 
-     * @return
-     */
-    public boolean needsUpdatingWhileSaving() {
-        return mNeedsUpdating;
-    }
-    
-    public long getLastSyncDateForProperties() {
-        return mLastSyncDateForProperties;
-    }
-    
-    public void setLastSyncDateForProperties(long lastSyncDate) {
-        mLastSyncDateForProperties = lastSyncDate;
-    }
-    
-    public long getLastSyncDateForData() {
-        return mLastSyncDateForData;
-    }
-
-    public void setLastSyncDateForData(long lastSyncDate) {
-        mLastSyncDateForData = lastSyncDate;
-    }
-
-    public void setKeepInSync(boolean keepInSync) {
-        mKeepInSync = keepInSync;
-    }
-    
-    public boolean keepInSync() {
-        return mKeepInSync;
-    }
-    
-    @Override
-    public int describeContents() {
-        return this.hashCode();
-    }
-
-    @Override
-    public int compareTo(OCFile another) {
-        if (isDirectory() && another.isDirectory()) {
-            return getRemotePath().toLowerCase().compareTo(another.getRemotePath().toLowerCase());
-        } else if (isDirectory()) {
-            return -1;
-        } else if (another.isDirectory()) {
-            return 1;
-        }
-        return getRemotePath().toLowerCase().compareTo(another.getRemotePath().toLowerCase());
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if(o instanceof OCFile){
-            OCFile that = (OCFile) o;
-            if(that != null){
-                return this.mId == that.mId;
-            }
-        }
-        
-        return false;
-    }
-
-    @Override
-    public String toString() {
-        String asString = "[id=%s, name=%s, mime=%s, downloaded=%s, local=%s, remote=%s, parentId=%s, keepInSinc=%s]";
-        asString = String.format(asString, Long.valueOf(mId), getFileName(), mMimeType, isDown(), mLocalPath, mRemotePath, Long.valueOf(mParentId), Boolean.valueOf(mKeepInSync));
-        return asString;
-    }
-
-    public String getEtag() {
-        return mEtag;
-    }
-
-    public long getLocalModificationTimestamp() {
-        if (mLocalPath != null && mLocalPath.length() > 0) {
-            File f = new File(mLocalPath);
-            return f.lastModified();
-        }
-        return 0;
-    }
-
-    /** @return  'True' if the file contains audio */
-    public boolean isAudio() {
-        return (mMimeType != null && mMimeType.startsWith("audio/"));
-    }
-
-    /** @return  'True' if the file contains video */
-    public boolean isVideo() {
-        return (mMimeType != null && mMimeType.startsWith("video/"));
-    }
-
-    /** @return  'True' if the file contains an image */
-    public boolean isImage() {
-        return ((mMimeType != null && mMimeType.startsWith("image/")) ||
-                 getMimeTypeFromName().startsWith("image/"));
-    }
-    
-    public String getMimeTypeFromName() {
-        String extension = "";
-        int pos = mRemotePath.lastIndexOf('.');
-        if (pos >= 0) {
-            extension = mRemotePath.substring(pos + 1);
-        }
-        String result = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase());
-        return (result != null) ? result : "";
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/db/DbHandler.java b/src/de/mobilcom/debitel/cloud/android/db/DbHandler.java
deleted file mode 100644 (file)
index c04c801..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.db;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-
-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();
-        mDatabaseName = MainApp.getDBName();
-    }
-
-    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;");
-
-        }
-    }
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/db/ProviderMeta.java b/src/de/mobilcom/debitel/cloud/android/db/ProviderMeta.java
deleted file mode 100644 (file)
index 28032c0..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-/* 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 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
-package de.mobilcom.debitel.cloud.android.db;\r
-\r
-import de.mobilcom.debitel.cloud.android.MainApp;\r
-\r
-import android.net.Uri;\r
-import android.provider.BaseColumns;\r
-\r
-/**\r
- * Meta-Class that holds various static field information\r
- * \r
- * @author Bartek Przybylski\r
- * \r
- */\r
-public class ProviderMeta {\r
-\r
-    /* These constants are now in MainApp\r
-        public static final String AUTHORITY_FILES = "org.owncloud";\r
-        public static final String DB_FILE = "owncloud.db";\r
-    */\r
-    public static final String DB_NAME = "filelist";\r
-    public static final int DB_VERSION = 4;\r
-\r
-    private ProviderMeta() {\r
-    }\r
-\r
-    static public class ProviderTableMeta implements BaseColumns {\r
-        public static final String DB_NAME = "filelist";\r
-        public static final Uri CONTENT_URI = Uri.parse("content://"\r
-                + MainApp.getAuthority() + "/");\r
-        public static final Uri CONTENT_URI_FILE = Uri.parse("content://"\r
-                + MainApp.getAuthority() + "/file");\r
-        public static final Uri CONTENT_URI_DIR = Uri.parse("content://"\r
-                + MainApp.getAuthority() + "/dir");\r
-\r
-        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.owncloud.file";\r
-        public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/vnd.owncloud.file";\r
-\r
-        public static final String FILE_PARENT = "parent";\r
-        public static final String FILE_NAME = "filename";\r
-        public static final String FILE_CREATION = "created";\r
-        public static final String FILE_MODIFIED = "modified";\r
-        public static final String FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA = "modified_at_last_sync_for_data";\r
-        public static final String FILE_CONTENT_LENGTH = "content_length";\r
-        public static final String FILE_CONTENT_TYPE = "content_type";\r
-        public static final String FILE_STORAGE_PATH = "media_path";\r
-        public static final String FILE_PATH = "path";\r
-        public static final String FILE_ACCOUNT_OWNER = "file_owner";\r
-        public static final String FILE_LAST_SYNC_DATE = "last_sync_date";  // _for_properties, but let's keep it as it is\r
-        public static final String FILE_LAST_SYNC_DATE_FOR_DATA = "last_sync_date_for_data";\r
-        public static final String FILE_KEEP_IN_SYNC = "keep_in_sync";\r
-\r
-        public static final String DEFAULT_SORT_ORDER = FILE_NAME\r
-                + " collate nocase asc";\r
-\r
-    }\r
-}\r
diff --git a/src/de/mobilcom/debitel/cloud/android/extensions/ExtensionsAvailableActivity.java b/src/de/mobilcom/debitel/cloud/android/extensions/ExtensionsAvailableActivity.java
deleted file mode 100644 (file)
index e75d39e..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.extensions;
-
-import android.os.Bundle;
-import android.support.v4.app.FragmentManager;
-
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-
-public class ExtensionsAvailableActivity extends SherlockFragmentActivity {
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        FragmentManager fm = getSupportFragmentManager();
-        ExtensionsAvailableDialog ead = new ExtensionsAvailableDialog();
-        ead.show(fm, "extensions_available_dialog");
-    }
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/extensions/ExtensionsAvailableDialog.java b/src/de/mobilcom/debitel/cloud/android/extensions/ExtensionsAvailableDialog.java
deleted file mode 100644 (file)
index e7e899f..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.extensions;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.ui.CustomButton;
-import android.content.Intent;
-import android.os.Bundle;
-import android.support.v4.app.DialogFragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.View.OnClickListener;
-
-public class ExtensionsAvailableDialog extends DialogFragment implements
-        OnClickListener {
-
-    public ExtensionsAvailableDialog() {
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        View view = inflater.inflate(R.layout.extensions_available_dialog,
-                container);
-        CustomButton btnYes = (CustomButton) view.findViewById(R.id.buttonYes);
-        CustomButton btnNo = (CustomButton) view.findViewById(R.id.buttonNo);
-        
-        btnYes.setOnClickListener(this);
-        btnNo.setOnClickListener(this);
-        getDialog().setTitle(R.string.extensions_avail_title);
-        return view;
-    }
-
-    @Override
-    public void onClick(View v) {
-        switch (v.getId()) {
-        case R.id.buttonYes: {
-            Intent i = new Intent(getActivity(), ExtensionsListActivity.class);
-            startActivity(i);
-            getActivity().finish();
-        }
-            break;
-        case R.id.buttonNo:
-            getActivity().finish();
-            break;
-        default:
-            Log_OC.e("EAD", "Button with unknown id clicked " + v.getId());
-        }
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/extensions/ExtensionsListActivity.java b/src/de/mobilcom/debitel/cloud/android/extensions/ExtensionsListActivity.java
deleted file mode 100644 (file)
index 040a86c..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.extensions;
-
-import java.util.HashMap;
-import java.util.LinkedList;
-
-import org.apache.commons.httpclient.HttpClient;
-import org.apache.commons.httpclient.methods.GetMethod;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.utils.OwnCloudVersion;
-
-
-import android.R;
-import android.app.ListActivity;
-import android.os.Bundle;
-import android.os.Handler;
-import android.widget.SimpleAdapter;
-
-public class ExtensionsListActivity extends ListActivity {
-
-    private static final String packages_url = "http://alefzero.eu/a/packages.php";
-
-    private Thread mGetterThread;
-    private final Handler mHandler = new Handler();
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mGetterThread = new Thread(new JsonGetter());
-        mGetterThread.start();
-    }
-
-    public void done(JSONArray a) {
-        LinkedList<HashMap<String, String>> ll = new LinkedList<HashMap<String, String>>();
-        for (int i = 0; i < a.length(); ++i) {
-            try {
-                ExtensionApplicationEntry ela = new ExtensionApplicationEntry(
-                        ((JSONObject) a.get(i)));
-                HashMap<String, String> ss = new HashMap<String, String>();
-                ss.put("NAME", ela.getName());
-                ss.put("DESC", ela.getDescription());
-                ll.add(ss);
-            } catch (JSONException e) {
-                e.printStackTrace();
-            }
-        }
-        setListAdapter(new SimpleAdapter(this, ll, R.layout.simple_list_item_2,
-                new String[] { "NAME", "DESC" }, new int[] {
-                        android.R.id.text1, android.R.id.text2 }));
-
-    }
-
-    private class JsonGetter implements Runnable {
-
-        @Override
-        public void run() {
-            HttpClient hc = new HttpClient();
-            GetMethod gm = new GetMethod(packages_url);
-            final JSONArray ar;
-            try {
-                hc.executeMethod(gm);
-                Log_OC.e("ASD", gm.getResponseBodyAsString() + "");
-                ar = new JSONObject(gm.getResponseBodyAsString())
-                        .getJSONArray("apps");
-            } catch (Exception e) {
-                e.printStackTrace();
-                return;
-            }
-
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    done(ar);
-                }
-            });
-
-        }
-
-    }
-
-    private class ExtensionApplicationEntry {
-        private static final String APP_NAME = "name";
-        private static final String APP_VERSION = "version";
-        private static final String APP_DESC = "description";
-        private static final String APP_ICON = "icon";
-        private static final String APP_URL = "download";
-        private static final String APP_PLAYID = "play_id";
-
-        private String mName, mDescription, mIcon, mDownload, mPlayId;
-        private OwnCloudVersion mVersion;
-
-        public ExtensionApplicationEntry(JSONObject appentry) {
-            try {
-                mName = appentry.getString(APP_NAME);
-                mDescription = appentry.getString(APP_DESC);
-                mIcon = appentry.getString(APP_ICON);
-                mDownload = appentry.getString(APP_URL);
-                mPlayId = appentry.getString(APP_PLAYID);
-                mVersion = new OwnCloudVersion(appentry.getString(APP_VERSION));
-            } catch (JSONException e) {
-                e.printStackTrace();
-            }
-        }
-
-        public String getName() {
-            return mName;
-        }
-
-        public String getDescription() {
-            return mDescription;
-        }
-
-        @SuppressWarnings("unused")
-        public String getIcon() {
-            return mIcon;
-        }
-
-        @SuppressWarnings("unused")
-        public String getDownload() {
-            return mDownload;
-        }
-
-        @SuppressWarnings("unused")
-        public String getPlayId() {
-            return mPlayId;
-        }
-
-        @SuppressWarnings("unused")
-        public OwnCloudVersion getVersion() {
-            return mVersion;
-        }
-    }
-    
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/files/BootupBroadcastReceiver.java b/src/de/mobilcom/debitel/cloud/android/files/BootupBroadcastReceiver.java
deleted file mode 100644 (file)
index 9dc26d8..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.files;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.files.services.FileObserverService;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-public class BootupBroadcastReceiver extends BroadcastReceiver {
-
-    private static String TAG = "BootupBroadcastReceiver";
-    
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        if (!intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
-            Log_OC.wtf(TAG, "Incorrect action sent " + intent.getAction());
-            return;
-        }
-        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_OC.d(TAG, "DONE");
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/files/FileHandler.java b/src/de/mobilcom/debitel/cloud/android/files/FileHandler.java
deleted file mode 100644 (file)
index 917ffea..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.files;
-
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-
-public interface FileHandler {
-
-    /**
-     * TODO
-     */
-    public void openFile(OCFile file);
-
-    
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/files/InstantUploadBroadcastReceiver.java b/src/de/mobilcom/debitel/cloud/android/files/InstantUploadBroadcastReceiver.java
deleted file mode 100644 (file)
index 3db31aa..0000000
+++ /dev/null
@@ -1,214 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.files;
-
-import java.io.File;
-
-
-import android.accounts.Account;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-//import android.content.IntentFilter;
-import android.database.Cursor;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo.State;
-import android.preference.PreferenceManager;
-import android.provider.MediaStore.Images.Media;
-import android.webkit.MimeTypeMap;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-import de.mobilcom.debitel.cloud.android.db.DbHandler;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader;
-import de.mobilcom.debitel.cloud.android.utils.FileStorageUtils;
-
-public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
-
-    private static String TAG = "InstantUploadBroadcastReceiver";
-    private static final String[] CONTENT_PROJECTION = { Media.DATA, Media.DISPLAY_NAME, Media.MIME_TYPE, Media.SIZE };
-    //Unofficial action, works for most devices but not HTC. See: https://github.com/owncloud/android/issues/6
-    private static String NEW_PHOTO_ACTION_UNOFFICIAL = "com.android.camera.NEW_PICTURE";
-    //Officially supported action since SDK 14: http://developer.android.com/reference/android/hardware/Camera.html#ACTION_NEW_PICTURE
-    private static String NEW_PHOTO_ACTION = "android.hardware.action.NEW_PICTURE";
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        Log_OC.d(TAG, "Received: " + intent.getAction());
-        
-        FileUploader fileUploader = new FileUploader();
-        
-        if (intent.getAction().equals(android.net.ConnectivityManager.CONNECTIVITY_ACTION)) {
-            handleConnectivityAction(context, intent);
-        }else if (intent.getAction().equals(NEW_PHOTO_ACTION_UNOFFICIAL)) {
-            handleNewPhotoAction(context, intent);
-            Log_OC.d(TAG, "UNOFFICIAL processed: com.android.camera.NEW_PICTURE");
-        } else if (intent.getAction().equals(NEW_PHOTO_ACTION)) {
-            handleNewPhotoAction(context, intent);
-            Log_OC.d(TAG, "OFFICIAL processed: android.hardware.action.NEW_PICTURE");
-        } else if (intent.getAction().equals(fileUploader.getUploadFinishMessage())) {
-            handleUploadFinished(context, intent);
-        } else {
-            Log_OC.e(TAG, "Incorrect intent sent: " + intent.getAction());
-        }
-    }
-
-    private void handleUploadFinished(Context context, Intent intent) {
-        // remove successfull uploading, ignore rest for reupload on reconnect
-        /*
-        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)) {
-                Log_OC.w(TAG, "Tried to remove non existing instant upload file " + localPath);
-            }
-            db.close();
-        }
-        */
-    }
-
-    private void handleNewPhotoAction(Context context, Intent intent) {
-        if (!instantUploadEnabled(context)) {
-            Log_OC.d(TAG, "Instant upload disabled, aborting uploading");
-            return;
-        }
-
-        Account account = AccountUtils.getCurrentOwnCloudAccount(context);
-        if (account == null) {
-            Log_OC.w(TAG, "No owncloud account found for instant upload, aborting");
-            return;
-        }
-
-        Cursor c = context.getContentResolver().query(intent.getData(), CONTENT_PROJECTION, null, null, null);
-
-        if (!c.moveToFirst()) {
-            Log_OC.e(TAG, "Couldn't resolve given uri: " + intent.getDataString());
-            return;
-        }
-
-        String file_path = c.getString(c.getColumnIndex(Media.DATA));
-        String file_name = c.getString(c.getColumnIndex(Media.DISPLAY_NAME));
-        String mime_type = c.getString(c.getColumnIndex(Media.MIME_TYPE));
-
-        c.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
-        // 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, 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);
-        context.startService(i);
-
-    }
-
-    private void handleConnectivityAction(Context context, Intent intent) {
-        if (!instantUploadEnabled(context)) {
-            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))) {
-            DbHandler db = new DbHandler(context);
-            Cursor c = db.getAwaitingFiles();
-            if (c.moveToFirst()) {
-                //IntentFilter filter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE);
-                //context.getApplicationContext().registerReceiver(this, filter);
-                do {
-                    String account_name = c.getString(c.getColumnIndex("account"));
-                    String file_path = c.getString(c.getColumnIndex("path"));
-                    File f = new File(file_path);
-                    if (f.exists()) {
-                        Account account = new Account(account_name, MainApp.getAccountType());
-
-                        String mimeType = null;
-                        try {
-                            mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
-                                    f.getName().substring(f.getName().lastIndexOf('.') + 1));
-
-                        } catch (Throwable e) {
-                            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, 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_OC.w(TAG, "Instant upload file " + f.getAbsolutePath() + " dont exist anymore");
-                    }
-                } while (c.moveToNext());
-            }
-            c.close();
-            db.close();
-        }
-
-    }
-
-    public static boolean isOnline(Context context) {
-        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
-        return cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnected();
-    }
-
-    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;
-    }
-
-    public static boolean instantUploadEnabled(Context context) {
-        return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_uploading", false);
-    }
-
-    public static boolean instantUploadViaWiFiOnly(Context context) {
-        return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_upload_on_wifi", false);
-    }
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/files/OwnCloudFileObserver.java b/src/de/mobilcom/debitel/cloud/android/files/OwnCloudFileObserver.java
deleted file mode 100644 (file)
index 79f4c18..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.files;
-
-import java.io.File;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult;
-import de.mobilcom.debitel.cloud.android.operations.SynchronizeFileOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-import de.mobilcom.debitel.cloud.android.ui.activity.ConflictsResolveActivity;
-
-
-import android.accounts.Account;
-import android.content.Context;
-import android.content.Intent;
-import android.os.FileObserver;
-
-public class OwnCloudFileObserver extends FileObserver {
-
-    public static int CHANGES_ONLY = CLOSE_WRITE;
-    
-    private static String TAG = OwnCloudFileObserver.class.getSimpleName();
-    
-    private String mPath;
-    private int mMask;
-    private Account mOCAccount;
-    //private OCFile mFile;
-    private Context mContext;
-
-    
-    public OwnCloudFileObserver(String path, Account account, Context context, int mask) {
-        super(path, mask);
-        if (path == null)
-            throw new IllegalArgumentException("NULL path argument received"); 
-        /*if (file == null)
-            throw new IllegalArgumentException("NULL file argument received");*/ 
-        if (account == null)
-            throw new IllegalArgumentException("NULL account argument received"); 
-        if (context == null)
-            throw new IllegalArgumentException("NULL context argument received");
-        /*if (!path.equals(file.getStoragePath()) && !path.equals(FileStorageUtils.getDefaultSavePathFor(account.name, file)))
-            throw new IllegalArgumentException("File argument is not linked to the local file set in path argument"); */
-        mPath = path;
-        //mFile = file;
-        mOCAccount = account;
-        mContext = context; 
-        mMask = mask;
-    }
-    
-    @Override
-    public void onEvent(int event, String path) {
-        Log_OC.d(TAG, "Got file modified with event " + event + " and path " + mPath + ((path != null) ? File.separator + path : ""));
-        if ((event & mMask) == 0) {
-            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;
-        }
-        FileDataStorageManager storageManager = new FileDataStorageManager(mOCAccount, mContext.getContentResolver());
-        OCFile file = storageManager.getFileByLocalPath(mPath);     // a fresh object is needed; many things could have occurred to the file since it was registered to observe
-                                                                    // again, assuming that local files are linked to a remote file AT MOST, SOMETHING TO BE DONE; 
-        SynchronizeFileOperation sfo = new SynchronizeFileOperation(file, 
-                                                                    null, 
-                                                                    storageManager, 
-                                                                    mOCAccount, 
-                                                                    true, 
-                                                                    true, 
-                                                                    mContext);
-        RemoteOperationResult result = sfo.execute(mOCAccount, mContext);
-        if (result.getCode() == ResultCode.SYNC_CONFLICT) {
-            // ISSUE 5: if the user is not running the app (this is a service!), this can be very intrusive; a notification should be preferred
-            Intent i = new Intent(mContext, ConflictsResolveActivity.class);
-            i.setFlags(i.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
-            i.putExtra(ConflictsResolveActivity.EXTRA_FILE, file);
-            i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, mOCAccount);
-            mContext.startActivity(i);
-        }
-        // TODO save other errors in some point where the user can inspect them later;
-        //      or maybe just toast them;
-        //      or nothing, very strange fails
-    }
-    
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/files/managers/OCNotificationManager.java b/src/de/mobilcom/debitel/cloud/android/files/managers/OCNotificationManager.java
deleted file mode 100644 (file)
index f1f57d2..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.files.managers;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.content.Context;
-import android.widget.RemoteViews;
-
-import de.mobilcom.debitel.cloud.android.R;
-
-public class OCNotificationManager {
-
-    enum NotificationType {
-        NOTIFICATION_SIMPLE,
-        NOTIFICATION_PROGRESS
-    }
-    
-    static public class NotificationData {
-        private String mText, mSubtitle;
-        private int mPercent;
-        private boolean mOngoing;
-
-        public NotificationData(String text, String subtitle, boolean ongoing) {
-            this(text, subtitle, -1, ongoing);
-        }
-        
-        public NotificationData(int percent, boolean ongoing) {
-            this(null, null, percent, ongoing);
-        }
-        
-        public NotificationData(String text, int percent, boolean ongoing) {
-            this(text, null, percent, ongoing);
-        }
-        
-        public NotificationData(String text, String subtitle, int percent, boolean ongoing) {
-            mText = text;
-            mPercent = percent;
-            mSubtitle = subtitle;
-            mOngoing = ongoing;
-        }
-        
-        public String getText() { return mText; }
-        public int getPercent() { return mPercent; }
-        public String getSubtitle() { return mSubtitle; }
-        public boolean getOngoing() { return mOngoing; }
-    }
-    
-    static private OCNotificationManager mInstance = null;
-
-    private class NotificationTypePair {
-        public Notification mNotificaiton;
-        public NotificationType mType;
-        public NotificationTypePair(Notification n, NotificationType type) {
-            mNotificaiton = n;
-            mType = type;
-        }
-    }
-    
-    private Context mContext;
-    private Map<Integer, NotificationTypePair> mNotificationMap;
-    private int mNotificationCounter;
-    NotificationManager mNM;
-    
-    static OCNotificationManager getInstance(Context context) {
-        if (mInstance == null)
-            mInstance = new OCNotificationManager(context);
-        return mInstance;
-    }
-    
-    OCNotificationManager(Context context) {
-        mContext = context;
-        mNotificationMap = new HashMap<Integer, NotificationTypePair>();
-        mNM = (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-        mNotificationCounter = 0;
-    }
-    
-    public int postNotification(NotificationType type, NotificationData data) {
-        mNotificationCounter++;
-        Notification notification = null;
-        
-        switch (type) {
-            case NOTIFICATION_SIMPLE:
-                notification = new Notification(R.drawable.icon, data.getText(), System.currentTimeMillis());
-                break;
-            case NOTIFICATION_PROGRESS:
-                notification = new Notification();
-                notification.contentView = new RemoteViews(mContext.getPackageName(), R.layout.progressbar_layout);
-                notification.contentView.setTextViewText(R.id.status_text,
-                                                         data.getText());
-                notification.contentView.setImageViewResource(R.id.status_icon,
-                                                              R.id.icon);
-                notification.contentView.setProgressBar(R.id.status_progress,
-                                                        100,
-                                                        data.getPercent(),
-                                                        false);
-                break;
-            default:
-                return -1;
-        }
-        if (data.getOngoing()) {
-            notification.flags |= notification.flags | Notification.FLAG_ONGOING_EVENT;
-        }
-        
-        mNotificationMap.put(mNotificationCounter, new NotificationTypePair(notification, type));
-        return mNotificationCounter;
-    }
-    
-    public boolean updateNotification(int notification_id, NotificationData data) {
-        if (!mNotificationMap.containsKey(notification_id)) {
-            return false;
-        }
-        NotificationTypePair pair = mNotificationMap.get(notification_id);
-        switch (pair.mType) {
-            case NOTIFICATION_PROGRESS:
-                pair.mNotificaiton.contentView.setProgressBar(R.id.status_text,
-                                                              100,
-                                                              data.getPercent(),
-                                                              false);
-                return true;
-            case NOTIFICATION_SIMPLE:
-                pair.mNotificaiton = new Notification(R.drawable.icon,
-                                                      data.getText(), System.currentTimeMillis());
-                mNM.notify(notification_id, pair.mNotificaiton);
-                return true;
-            default:
-                return false;
-        }
-    }
-    
-    public void discardNotification(int notification_id) {
-        mNM.cancel(notification_id);
-        mNotificationMap.remove(notification_id);
-    }
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/files/services/FileDownloader.java b/src/de/mobilcom/debitel/cloud/android/files/services/FileDownloader.java
deleted file mode 100644 (file)
index b004070..0000000
+++ /dev/null
@@ -1,548 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.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 eu.alefzero.webdav.OnDatatransferProgressListener;
-
-
-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 de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.authentication.AuthenticatorActivity;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.network.OwnCloudClientUtils;
-import de.mobilcom.debitel.cloud.android.operations.DownloadFileOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileActivity;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileDisplayActivity;
-import de.mobilcom.debitel.cloud.android.ui.preview.PreviewImageActivity;
-import de.mobilcom.debitel.cloud.android.ui.preview.PreviewImageFragment;
-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";
-    
-    private static final String DOWNLOAD_ADDED_MESSAGE = "DOWNLOAD_ADDED";
-    private 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;
-    
-    
-    public String getDownloadAddedMessage() {
-        return getClass().getName().toString() + DOWNLOAD_ADDED_MESSAGE;
-    }
-    
-    public String getDownloadFinishMessage() {
-        return getClass().getName().toString() + DOWNLOAD_FINISH_MESSAGE;
-    }
-    
-    /**
-     * 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, FileDisplayActivity.class);
-        }
-        showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, download.getFile());
-        showDetailsIntent.putExtra(FileActivity.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 ||
-                                                // (downloadResult.isTemporalRedirection() && downloadResult.isIdPRedirection()
-                                                  (downloadResult.isIdPRedirection()
-                                                        && MainApp.getAuthTokenTypeSamlSessionCookie().equals(mDownloadClient.getAuthTokenType())));
-            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_ENFORCED_UPDATE, true);
-                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, FileDisplayActivity.class);
-                    }
-                    showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, download.getFile());
-                    showDetailsIntent.putExtra(FileActivity.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(getDownloadFinishMessage());
-        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(getDownloadAddedMessage());
-        added.putExtra(ACCOUNT_NAME, download.getAccount().name);
-        added.putExtra(EXTRA_REMOTE_PATH, download.getRemotePath());
-        added.putExtra(EXTRA_FILE_PATH, download.getSavePath());
-        sendStickyBroadcast(added);
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/files/services/FileObserverService.java b/src/de/mobilcom/debitel/cloud/android/files/services/FileObserverService.java
deleted file mode 100644 (file)
index 773faaa..0000000
+++ /dev/null
@@ -1,284 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.files.services;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.Map;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.db.ProviderMeta.ProviderTableMeta;
-import de.mobilcom.debitel.cloud.android.files.OwnCloudFileObserver;
-import de.mobilcom.debitel.cloud.android.operations.SynchronizeFileOperation;
-import de.mobilcom.debitel.cloud.android.utils.FileStorageUtils;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.app.Service;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.Cursor;
-import android.os.Binder;
-import android.os.IBinder;
-
-public class FileObserverService extends Service {
-
-    public final static int CMD_INIT_OBSERVED_LIST = 1;
-    public final static int CMD_ADD_OBSERVED_FILE = 2;
-    public final static int CMD_DEL_OBSERVED_FILE = 3;
-
-    public final static String KEY_FILE_CMD = "KEY_FILE_CMD";
-    public final static String KEY_CMD_ARG_FILE = "KEY_CMD_ARG_FILE";
-    public final static String KEY_CMD_ARG_ACCOUNT = "KEY_CMD_ARG_ACCOUNT";
-
-    private static String TAG = FileObserverService.class.getSimpleName();
-
-    private static Map<String, OwnCloudFileObserver> mObserversMap;
-    private static DownloadCompletedReceiverBis mDownloadReceiver;
-    private IBinder mBinder = new LocalBinder();
-    
-    private String mDownloadAddedMessage;
-    private String mDownloadFinishMessage;
-
-    public class LocalBinder extends Binder {
-        FileObserverService getService() {
-            return FileObserverService.this;
-        }
-    }
-    
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mDownloadReceiver = new DownloadCompletedReceiverBis();
-        
-        FileDownloader downloader = new FileDownloader();
-        mDownloadAddedMessage = downloader.getDownloadAddedMessage();
-        mDownloadFinishMessage= downloader.getDownloadFinishMessage();
-        
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(mDownloadAddedMessage);
-        filter.addAction(mDownloadFinishMessage);        
-        registerReceiver(mDownloadReceiver, filter);
-        
-        mObserversMap = new HashMap<String, OwnCloudFileObserver>();
-        //initializeObservedList();
-    }
-    
-    
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-        unregisterReceiver(mDownloadReceiver);
-        mObserversMap = null;   // TODO study carefully the life cycle of Services to grant the best possible observance
-        Log_OC.d(TAG, "Bye, bye");
-    }
-    
-    
-    @Override
-    public IBinder onBind(Intent intent) {
-        return mBinder;
-    }
-
-    @Override
-    public int onStartCommand(Intent intent, int flags, int startId) {
-        // this occurs when system tries to restart
-        // service, so we need to reinitialize observers
-        if (intent == null) {
-            initializeObservedList();
-            return Service.START_STICKY;
-        }
-            
-        if (!intent.hasExtra(KEY_FILE_CMD)) {
-            Log_OC.e(TAG, "No KEY_FILE_CMD argument given");
-            return Service.START_STICKY;
-        }
-
-        switch (intent.getIntExtra(KEY_FILE_CMD, -1)) {
-            case CMD_INIT_OBSERVED_LIST:
-                initializeObservedList();
-                break;
-            case CMD_ADD_OBSERVED_FILE:
-                addObservedFile( (OCFile)intent.getParcelableExtra(KEY_CMD_ARG_FILE), 
-                                 (Account)intent.getParcelableExtra(KEY_CMD_ARG_ACCOUNT));
-                break;
-            case CMD_DEL_OBSERVED_FILE:
-                removeObservedFile( (OCFile)intent.getParcelableExtra(KEY_CMD_ARG_FILE), 
-                                    (Account)intent.getParcelableExtra(KEY_CMD_ARG_ACCOUNT));
-                break;
-            default:
-                Log_OC.wtf(TAG, "Incorrect key given");
-        }
-
-        return Service.START_STICKY;
-    }
-
-    
-    /**
-     * Read from the local database the list of files that must to be kept synchronized and 
-     * starts file observers to monitor local changes on them
-     */
-    private void initializeObservedList() {
-        mObserversMap.clear();
-        Cursor c = getContentResolver().query(
-                ProviderTableMeta.CONTENT_URI,
-                null,
-                ProviderTableMeta.FILE_KEEP_IN_SYNC + " = ?",
-                new String[] {String.valueOf(1)},
-                null);
-        if (c == null || !c.moveToFirst()) return;
-        AccountManager acm = AccountManager.get(this);
-        Account[] accounts = acm.getAccounts();
-        do {
-            Account account = null;
-            for (Account a : accounts)
-                if (a.name.equals(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ACCOUNT_OWNER)))) {
-                    account = a;
-                    break;
-                }
-
-            if (account == null) continue;
-            FileDataStorageManager storage =
-                    new FileDataStorageManager(account, getContentResolver());
-            if (!storage.fileExists(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PATH))))
-                continue;
-
-            String path = c.getString(c.getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH));
-            if (path == null || path.length() <= 0)
-                continue;
-            OwnCloudFileObserver observer =
-                    new OwnCloudFileObserver(   path, 
-                                                account, 
-                                                getApplicationContext(), 
-                                                OwnCloudFileObserver.CHANGES_ONLY);
-            mObserversMap.put(path, observer);
-            if (new File(path).exists()) {
-                observer.startWatching();
-                Log_OC.d(TAG, "Started watching file " + path);
-            }
-            
-        } while (c.moveToNext());
-        c.close();
-    }
-    
-    
-    /**
-     * Registers the local copy of a remote file to be observed for local changes,
-     * an automatically updated in the ownCloud server.
-     * 
-     * This method does NOT perform a {@link SynchronizeFileOperation} over the file. 
-     *
-     * TODO We are ignoring that, currently, a local file can be linked to different files
-     * in ownCloud if it's uploaded several times. That's something pending to update: we 
-     * will avoid that the same local file is linked to different remote files.
-     * 
-     * @param file      Object representing a remote file which local copy must be observed.
-     * @param account   OwnCloud account containing file.
-     */
-    private void addObservedFile(OCFile file, Account account) {
-        if (file == null) {
-            Log_OC.e(TAG, "Trying to add a NULL file to observer");
-            return;
-        }
-        String localPath = file.getStoragePath();
-        if (localPath == null || localPath.length() <= 0) { // file downloading / to be download for the first time
-            localPath = FileStorageUtils.getDefaultSavePathFor(account.name, file);
-        }
-        OwnCloudFileObserver observer = mObserversMap.get(localPath);
-        if (observer == null) {
-            /// the local file was never registered to observe before
-            observer = new OwnCloudFileObserver(    localPath, 
-                                                    account, 
-                                                    getApplicationContext(), 
-                                                    OwnCloudFileObserver.CHANGES_ONLY);
-            mObserversMap.put(localPath, observer);
-            Log_OC.d(TAG, "Observer added for path " + localPath);
-        
-            if (file.isDown()) {
-                observer.startWatching();
-                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
-        }
-        
-    }
-
-    
-    /**
-     * Unregisters the local copy of a remote file to be observed for local changes.
-     *
-     * Starts to watch it, if the file has a local copy to watch.
-     * 
-     * TODO We are ignoring that, currently, a local file can be linked to different files
-     * in ownCloud if it's uploaded several times. That's something pending to update: we 
-     * will avoid that the same local file is linked to different remote files.
-     *
-     * @param file      Object representing a remote file which local copy must be not observed longer.
-     * @param account   OwnCloud account containing file.
-     */
-    private void removeObservedFile(OCFile file, Account account) {
-        if (file == null) {
-            Log_OC.e(TAG, "Trying to remove a NULL file");
-            return;
-        }
-        String localPath = file.getStoragePath();
-        if (localPath == null || localPath.length() <= 0) {
-            localPath = FileStorageUtils.getDefaultSavePathFor(account.name, file);
-        }
-        
-        OwnCloudFileObserver observer = mObserversMap.get(localPath);
-        if (observer != null) {
-            observer.stopWatching();
-            mObserversMap.remove(observer);
-            Log_OC.d(TAG, "Stopped watching " + localPath);
-        }
-        
-    }
-
-
-    /**
-     *  Private receiver listening to events broadcast by the FileDownloader service.
-     * 
-     *  Starts and stops the observance on registered files when they are being download,
-     *  in order to avoid to start unnecessary synchronizations. 
-     */
-    private class DownloadCompletedReceiverBis extends BroadcastReceiver {
-        
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String downloadPath = intent.getStringExtra(FileDownloader.EXTRA_FILE_PATH);
-            OwnCloudFileObserver observer = mObserversMap.get(downloadPath);
-            if (observer != null) {
-                if (intent.getAction().equals(mDownloadFinishMessage) &&
-                        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_OC.d(TAG, "Watching again " + downloadPath);
-                
-                } else if (intent.getAction().equals(mDownloadAddedMessage)) {
-                    observer.stopWatching();
-                    Log_OC.d(TAG, "Disabling observance of " + downloadPath);
-                } 
-            }
-        }
-        
-    }
-    
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/files/services/FileUploader.java b/src/de/mobilcom/debitel/cloud/android/files/services/FileUploader.java
deleted file mode 100644 (file)
index 26f29ba..0000000
+++ /dev/null
@@ -1,923 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.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 org.apache.http.HttpStatus;
-import org.apache.jackrabbit.webdav.DavConstants;
-import org.apache.jackrabbit.webdav.MultiStatus;
-import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
-
-
-import eu.alefzero.webdav.OnDatatransferProgressListener;
-import eu.alefzero.webdav.WebdavEntry;
-import eu.alefzero.webdav.WebdavUtils;
-
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-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.webkit.MimeTypeMap;
-import android.widget.RemoteViews;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.authentication.AccountAuthenticator;
-import de.mobilcom.debitel.cloud.android.authentication.AuthenticatorActivity;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.db.DbHandler;
-import de.mobilcom.debitel.cloud.android.network.OwnCloudClientUtils;
-import de.mobilcom.debitel.cloud.android.operations.ChunkedUploadFileOperation;
-import de.mobilcom.debitel.cloud.android.operations.CreateFolderOperation;
-import de.mobilcom.debitel.cloud.android.operations.ExistenceCheckOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult;
-import de.mobilcom.debitel.cloud.android.operations.UploadFileOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-import de.mobilcom.debitel.cloud.android.ui.activity.FailedUploadActivity;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileActivity;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileDisplayActivity;
-import de.mobilcom.debitel.cloud.android.ui.activity.InstantUploadActivity;
-import de.mobilcom.debitel.cloud.android.ui.preview.PreviewImageActivity;
-import de.mobilcom.debitel.cloud.android.ui.preview.PreviewImageFragment;
-import de.mobilcom.debitel.cloud.android.utils.OwnCloudVersion;
-
-import eu.alefzero.webdav.WebdavClient;
-
-public class FileUploader extends Service implements OnDatatransferProgressListener {
-
-    private static final String UPLOAD_FINISH_MESSAGE = "UPLOAD_FINISH";
-    public static final String EXTRA_UPLOAD_RESULT = "RESULT";
-    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 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;
-
-    public static final int UPLOAD_SINGLE_FILE = 0;
-    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;
-    private WebdavClient mUploadClient = null;
-    private Account mLastAccount = null;
-    private FileDataStorageManager mStorageManager;
-
-    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;
-
-    
-    public String getUploadFinishMessage() {
-        return getClass().getName().toString() + UPLOAD_FINISH_MESSAGE;
-    }
-    
-    /**
-     * Builds a key for mPendingUploads from the account and file to upload
-     * 
-     * @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();
-    }
-
-    private String buildRemoteName(Account account, String remotePath) {
-        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.
-     */
-    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);
-        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.
-     */
-    @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_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_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) };
-
-            } 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?
-
-            } else {
-                localPaths = intent.getStringArrayExtra(KEY_LOCAL_FILE);
-                remotePaths = intent.getStringArrayExtra(KEY_REMOTE_FILE);
-                mimeTypes = intent.getStringArrayExtra(KEY_MIME_TYPE);
-            }
-        }
-
-        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);
-        
-        if (intent.hasExtra(KEY_FILE) && files == null) {
-            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_OC.e(TAG, "Incorrect array for local paths provided in upload intent");
-                return Service.START_NOT_STICKY;
-            }
-            if (remotePaths == null) {
-                Log_OC.e(TAG, "Incorrect array for remote paths provided in upload intent");
-                return Service.START_NOT_STICKY;
-            }
-            if (localPaths.length != remotePaths.length) {
-                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);
-                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));
-        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++) {
-                uploadKey = buildRemoteName(account, files[i].getRemotePath());
-                if (chunked) {
-                    newUpload = new ChunkedUploadFileOperation(account, files[i], isInstant, forceOverwrite,
-                            localAction);
-                } else {
-                    newUpload = new UploadFileOperation(account, files[i], isInstant, forceOverwrite, localAction);
-                }
-                if (isInstant) {
-                    newUpload.setRemoteFolderToBeCreated();
-                }
-                mPendingUploads.putIfAbsent(uploadKey, newUpload); // Grants that the file only upload once time
-
-                newUpload.addDatatransferProgressListener(this);
-                newUpload.addDatatransferProgressListener((FileUploaderBinder)mBinder);
-                requestedUploads.add(uploadKey);
-            }
-
-        } catch (IllegalArgumentException e) {
-            Log_OC.e(TAG, "Not enough information provided in intent: " + e.getMessage());
-            return START_NOT_STICKY;
-
-        } catch (IllegalStateException e) {
-            Log_OC.e(TAG, "Bad information provided in intent: " + e.getMessage());
-            return START_NOT_STICKY;
-
-        } catch (Exception 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.
-     * 
-     * 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.
-     * 
-     * It provides by itself the available operations.
-     */
-    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
-         */
-        public void cancel(Account account, OCFile file) {
-            UploadFileOperation upload = null;
-            synchronized (mPendingUploads) {
-                upload = mPendingUploads.remove(buildRemoteName(account, file));
-            }
-            if (upload != null) {
-                upload.cancel();
-            }
-        }
-        
-        
-        
-        public void clearListeners() {
-            mBoundListeners.clear();
-        }
-
-
-        
-        
-        /**
-         * Returns True when the file described by 'file' is being uploaded to
-         * the ownCloud account 'account' or waiting for it
-         * 
-         * If 'file' is a directory, returns 'true' if some of its descendant files is 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
-         */
-        public boolean isUploading(Account account, OCFile file) {
-            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 uploads :(
-                    Iterator<String> it = mPendingUploads.keySet().iterator();
-                    boolean found = false;
-                    while (it.hasNext() && !found) {
-                        found = it.next().startsWith(targetKey);
-                    }
-                    return found;
-                } else {
-                    return (mPendingUploads.containsKey(targetKey));
-                }
-            }
-        }
-
-
-        /**
-         * 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.
-     * 
-     * 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
-        FileUploader mService;
-
-        public ServiceHandler(Looper looper, FileUploader 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> requestedUploads = (AbstractList<String>) msg.obj;
-            if (msg.obj != null) {
-                Iterator<String> it = requestedUploads.iterator();
-                while (it.hasNext()) {
-                    mService.uploadFile(it.next());
-                }
-            }
-            mService.stopSelf(msg.arg1);
-        }
-    }
-
-    /**
-     * Core upload method: sends the file(s) to upload
-     * 
-     * @param uploadKey Key to access the upload to perform, contained in
-     *            mPendingUploads
-     */
-    public void uploadFile(String uploadKey) {
-
-        synchronized (mPendingUploads) {
-            mCurrentUpload = mPendingUploads.get(uploadKey);
-        }
-
-        if (mCurrentUpload != null) {
-
-            notifyUploadStart(mCurrentUpload);
-
-            RemoteOperationResult uploadResult = null, grantResult = null;
-            
-            try {
-                /// prepare client object to send requests to the ownCloud server
-                if (mUploadClient == null || !mLastAccount.equals(mCurrentUpload.getAccount())) {
-                    mLastAccount = mCurrentUpload.getAccount();
-                    mStorageManager = new FileDataStorageManager(mLastAccount, getContentResolver());
-                    mUploadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext());
-                }
-                
-                /// check the existence of the parent folder for the file to upload
-                String remoteParentPath = new File(mCurrentUpload.getRemotePath()).getParent();
-                remoteParentPath = remoteParentPath.endsWith(OCFile.PATH_SEPARATOR) ? remoteParentPath : remoteParentPath + OCFile.PATH_SEPARATOR;
-                grantResult = grantFolderExistence(remoteParentPath);
-            
-                /// perform the upload
-                if (grantResult.isSuccess()) {
-                    OCFile parent = mStorageManager.getFileByPath(remoteParentPath);
-                    mCurrentUpload.getFile().setParentId(parent.getFileId());
-                    uploadResult = mCurrentUpload.execute(mUploadClient);
-                    if (uploadResult.isSuccess()) {
-                        saveUploadedFile();
-                    }
-                } else {
-                    uploadResult = grantResult;
-                }
-                
-            } catch (AccountsException e) {
-                Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
-                uploadResult = new RemoteOperationResult(e);
-                
-            } catch (IOException e) {
-                Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
-                uploadResult = new RemoteOperationResult(e);
-                
-            } finally {
-                synchronized (mPendingUploads) {
-                    mPendingUploads.remove(uploadKey);
-                    Log_OC.i(TAG, "Remove CurrentUploadItem from pending upload Item Map.");
-                }
-                if (uploadResult.isException()) {
-                    // enforce the creation of a new client object for next uploads; this grant that a new socket will 
-                    // be created in the future if the current exception is due to an abrupt lose of network connection
-                    mUploadClient = null;
-                }
-            }
-            
-            /// notify result
-            
-            notifyUploadResult(uploadResult, mCurrentUpload);
-            sendFinalBroadcast(mCurrentUpload, uploadResult);
-
-        }
-
-    }
-
-    /**
-     * Checks the existence of the folder where the current file will be uploaded both in the remote server 
-     * and in the local database.
-     * 
-     * If the upload is set to enforce the creation of the folder, the method tries to create it both remote
-     * and locally.
-     *  
-     *  @param  pathToGrant     Full remote path whose existence will be granted.
-     *  @return  An {@link OCFile} instance corresponding to the folder where the file will be uploaded.
-     */
-    private RemoteOperationResult grantFolderExistence(String pathToGrant) {
-        RemoteOperation operation = new ExistenceCheckOperation(pathToGrant, this, false);
-        RemoteOperationResult result = operation.execute(mUploadClient);
-        if (!result.isSuccess() && result.getCode() == ResultCode.FILE_NOT_FOUND && mCurrentUpload.isRemoteFolderToBeCreated()) {
-            operation = new CreateFolderOperation(  pathToGrant,
-                                                    true,
-                                                    mStorageManager    );
-            result = operation.execute(mUploadClient);
-        }
-        if (result.isSuccess()) {
-            OCFile parentDir = mStorageManager.getFileByPath(pathToGrant);
-            if (parentDir == null) {
-                parentDir = createLocalFolder(pathToGrant);
-            }
-            if (parentDir != null) {
-                result = new RemoteOperationResult(ResultCode.OK);
-            } else {
-                result = new RemoteOperationResult(ResultCode.UNKNOWN_ERROR);
-            }
-        }
-        return result;
-    }
-
-    
-    private OCFile createLocalFolder(String remotePath) {
-        String parentPath = new File(remotePath).getParent();
-        parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR;
-        OCFile parent = mStorageManager.getFileByPath(parentPath);
-        if (parent == null) {
-            parent = createLocalFolder(parentPath);
-        }
-        if (parent != null) {
-            OCFile createdFolder = new OCFile(remotePath);
-            createdFolder.setMimetype("DIR");
-            createdFolder.setParentId(parent.getFileId());
-            mStorageManager.saveFile(createdFolder);
-            return createdFolder;
-        }
-        return null;
-    }
-    
-
-    /**
-     * 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)
-     * 
-     * TODO refactor this ugly thing
-     */
-    private void saveUploadedFile() {
-        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
-        PropFindMethod propfind = null;
-        RemoteOperationResult result = null;
-        try {
-            propfind = new PropFindMethod(mUploadClient.getBaseUri() + WebdavUtils.encodePath(mCurrentUpload.getRemotePath()),
-                    DavConstants.PROPFIND_ALL_PROP,
-                    DavConstants.DEPTH_0);
-            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, propfind.getResponseHeaders());
-            Log_OC.i(TAG, "Update: synchronizing properties for uploaded " + mCurrentUpload.getRemotePath() + ": "
-                    + result.getLogMessage());
-
-        } catch (Exception e) {
-            result = new RemoteOperationResult(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
-        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()
-        }
-
-        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(mCurrentUpload.getEtag());    // TODO Etag, where available
-    }
-
-    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
-
-        // MIME type
-        if (mimeType == null || mimeType.length() <= 0) {
-            try {
-                mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
-                        remotePath.substring(remotePath.lastIndexOf('.') + 1));
-            } catch (IndexOutOfBoundsException e) {
-                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);
-
-        return newFile;
-    }
-
-    /**
-     * Creates a status notification to show the upload progress
-     * 
-     * @param upload Upload operation starting.
-     */
-    @SuppressWarnings("deprecation")
-    private void notifyUploadStart(UploadFileOperation upload) {
-        // / 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.flags |= Notification.FLAG_ONGOING_EVENT;
-        mDefaultNotificationContentView = mNotification.contentView;
-        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.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, FileDisplayActivity.class);
-        showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, upload.getFile());
-        showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, upload.getAccount());
-        showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-        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));
-        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);
-            mNotification.contentView.setTextViewText(R.id.status_text, text);
-            mNotificationManager.notify(R.string.uploader_upload_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 an 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
-            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
-            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 = null;
-            if (PreviewImageFragment.canBePreviewed(upload.getFile())) {
-                showDetailsIntent = new Intent(this, PreviewImageActivity.class); 
-            } else {
-                showDetailsIntent = new Intent(this, FileDisplayActivity.class); 
-            }
-            showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, upload.getFile());
-            showDetailsIntent.putExtra(FileActivity.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
-            DbHandler db = new DbHandler(this.getBaseContext());
-            db.removeIUPendingFile(mCurrentUpload.getOriginalStoragePath());
-            db.close();
-
-        } else {
-
-            // / 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());
-            finalNotification.flags |= Notification.FLAG_AUTO_CANCEL;
-            String content = null;
-            
-            boolean needsToUpdateCredentials = (uploadResult.getCode() == ResultCode.UNAUTHORIZED ||
-                    //(uploadResult.isTemporalRedirection() && uploadResult.isIdPRedirection() && 
-                    (uploadResult.isIdPRedirection() &&
-                            MainApp.getAuthTokenTypeSamlSessionCookie().equals(mUploadClient.getAuthTokenType())));
-            if (needsToUpdateCredentials) {
-                // let the user update credentials with one click
-                Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
-                updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, upload.getAccount());
-                updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ENFORCED_UPDATE, true);
-                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);
-                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);
-                mUploadClient = null;   // grant that future retries on the same account will get the fresh credentials
-            } else {
-                // TODO put something smart in the contentIntent below
-            //    finalNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);
-            //}
-            
-                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());
-                }
-
-                // 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);
-        }
-
-    }
-
-    /**
-     * 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
-     */
-    private void sendFinalBroadcast(UploadFileOperation upload, RemoteOperationResult uploadResult) {
-        Intent end = new Intent(getUploadFinishMessage());
-        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());
-        }
-        end.putExtra(EXTRA_OLD_FILE_PATH, upload.getOriginalStoragePath());
-        end.putExtra(ACCOUNT_NAME, upload.getAccount().name);
-        end.putExtra(EXTRA_UPLOAD_RESULT, uploadResult.isSuccess());
-        sendStickyBroadcast(end);
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/files/services/OnUploadCompletedListener.java b/src/de/mobilcom/debitel/cloud/android/files/services/OnUploadCompletedListener.java
deleted file mode 100644 (file)
index 7f8039e..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.files.services;
-
-public interface OnUploadCompletedListener extends Runnable {
-
-    public boolean getUploadResult();
-
-    public void setUploadResult(boolean result);
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/location/LocationServiceLauncherReciever.java b/src/de/mobilcom/debitel/cloud/android/location/LocationServiceLauncherReciever.java
deleted file mode 100644 (file)
index 0c77a25..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.location;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-
-import android.app.ActivityManager;
-import android.app.ActivityManager.RunningServiceInfo;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
-
-public class LocationServiceLauncherReciever extends BroadcastReceiver {
-
-    private final String TAG = getClass().getSimpleName();
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        Intent deviceTrackingIntent = new Intent();
-        deviceTrackingIntent
-                .setAction("de.mobilcom.debitel.cloud.android.location.LocationUpdateService");
-        SharedPreferences preferences = PreferenceManager
-                .getDefaultSharedPreferences(context);
-        boolean trackDevice = preferences.getBoolean("enable_devicetracking",
-                true);
-
-        // Used in Preferences activity so that tracking is disabled or
-        // reenabled
-        if (intent.hasExtra("TRACKING_SETTING")) {
-            trackDevice = intent.getBooleanExtra("TRACKING_SETTING", true);
-        }
-
-        startOrStopDeviceTracking(context, trackDevice);
-    }
-
-    /**
-     * Used internally. Starts or stops the device tracking service
-     * 
-     * @param trackDevice true to start the service, false to stop it
-     */
-    private void startOrStopDeviceTracking(Context context, boolean trackDevice) {
-        Intent deviceTrackingIntent = new Intent();
-        deviceTrackingIntent
-                .setAction("de.mobilcom.debitel.cloud.android.location.LocationUpdateService");
-        if (!isDeviceTrackingServiceRunning(context) && trackDevice) {
-            Log_OC.d(TAG, "Starting device tracker service");
-            context.startService(deviceTrackingIntent);
-        } else if (isDeviceTrackingServiceRunning(context) && !trackDevice) {
-            Log_OC.d(TAG, "Stopping device tracker service");
-            context.stopService(deviceTrackingIntent);
-        }
-    }
-
-    /**
-     * Checks to see whether or not the LocationUpdateService is running
-     * 
-     * @return true, if it is. Otherwise false
-     */
-    private boolean isDeviceTrackingServiceRunning(Context context) {
-        ActivityManager manager = (ActivityManager) context
-                .getSystemService(Context.ACTIVITY_SERVICE);
-        for (RunningServiceInfo service : manager
-                .getRunningServices(Integer.MAX_VALUE)) {
-            if (getClass().getName().equals(service.service.getClassName())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/location/LocationUpdateService.java b/src/de/mobilcom/debitel/cloud/android/location/LocationUpdateService.java
deleted file mode 100644 (file)
index dccfe7f..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.location;
-
-import android.app.IntentService;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.location.Criteria;
-import android.location.Location;
-import android.location.LocationListener;
-import android.location.LocationManager;
-import android.location.LocationProvider;
-import android.os.Bundle;
-import android.preference.PreferenceManager;
-import android.widget.Toast;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-
-public class LocationUpdateService extends IntentService implements
-        LocationListener {
-
-    public static final String TAG = "LocationUpdateService";
-
-    private LocationManager mLocationManager;
-    private LocationProvider mLocationProvider;
-    private SharedPreferences mPreferences;
-
-    public LocationUpdateService() {
-        super(TAG);
-    }
-
-    @Override
-    protected void onHandleIntent(Intent intent) {
-        mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
-        // Determine, how we can track the device
-        Criteria criteria = new Criteria();
-        criteria.setAccuracy(Criteria.ACCURACY_FINE);
-        criteria.setPowerRequirement(Criteria.POWER_LOW);
-        mLocationProvider = mLocationManager.getProvider(mLocationManager
-                .getBestProvider(criteria, true));
-
-        // Notify user if there is no way to track the device
-        if (mLocationProvider == null) {
-            String message = String.format(getString(R.string.location_no_provider), getString(R.string.app_name));
-            Toast.makeText(this,
-                    message,
-                    Toast.LENGTH_LONG).show();
-            stopSelf();
-            return;
-        }
-
-        // Get preferences for device tracking
-        mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
-        boolean trackDevice = mPreferences.getBoolean("enable_devicetracking",
-                true);
-        int updateIntervall = Integer.parseInt(mPreferences.getString(
-                "devicetracking_update_intervall", "30")) * 60 * 1000;
-        int distanceBetweenLocationChecks = 50;
-
-        // If we do shall track the device -> Stop
-        if (!trackDevice) {
-            Log_OC.d(TAG, "Devicetracking is disabled");
-            stopSelf();
-            return;
-        }
-
-        mLocationManager.requestLocationUpdates(mLocationProvider.getName(),
-                updateIntervall, distanceBetweenLocationChecks, this);
-    }
-
-    @Override
-    public void onLocationChanged(Location location) {
-        Log_OC.d(TAG, "Location changed: " + location);
-
-    }
-
-    @Override
-    public void onProviderDisabled(String arg0) {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void onProviderEnabled(String arg0) {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
-        // TODO Auto-generated method stub
-
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/media/MediaControlView.java b/src/de/mobilcom/debitel/cloud/android/media/MediaControlView.java
deleted file mode 100644 (file)
index 5e24907..0000000
+++ /dev/null
@@ -1,472 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.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 de.mobilcom.debitel.cloud.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/de/mobilcom/debitel/cloud/android/media/MediaService.java b/src/de/mobilcom/debitel/cloud/android/media/MediaService.java
deleted file mode 100644 (file)
index a5094ce..0000000
+++ /dev/null
@@ -1,705 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.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 de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileActivity;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileDisplayActivity;
-
-/**
- * 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() : "de.mobilcom.debitel.cloud.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, FileDisplayActivity.class);
-        showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, mFile);
-        showDetailsIntent.putExtra(FileActivity.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, FileDisplayActivity.class);
-        showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, mFile);
-        showDetailsIntent.putExtra(FileActivity.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/de/mobilcom/debitel/cloud/android/media/MediaServiceBinder.java b/src/de/mobilcom/debitel/cloud/android/media/MediaServiceBinder.java
deleted file mode 100644 (file)
index 935d3e2..0000000
+++ /dev/null
@@ -1,181 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.media;
-
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.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);
-    }
-
-}
-
-
diff --git a/src/de/mobilcom/debitel/cloud/android/network/AdvancedSslSocketFactory.java b/src/de/mobilcom/debitel/cloud/android/network/AdvancedSslSocketFactory.java
deleted file mode 100644 (file)
index cf802ce..0000000
+++ /dev/null
@@ -1,240 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.network;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.net.SocketAddress;
-import java.net.UnknownHostException;
-import java.security.cert.X509Certificate;
-
-import javax.net.SocketFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLHandshakeException;
-import javax.net.ssl.SSLPeerUnverifiedException;
-import javax.net.ssl.SSLSession;
-import javax.net.ssl.SSLSocket;
-
-import org.apache.commons.httpclient.ConnectTimeoutException;
-import org.apache.commons.httpclient.params.HttpConnectionParams;
-import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
-import org.apache.http.conn.ssl.X509HostnameVerifier;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-
-/**
- * AdvancedSSLProtocolSocketFactory allows to create SSL {@link Socket}s with 
- * a custom SSLContext and an optional Hostname Verifier.
- * 
- * @author David A. Velasco
- */
-
-public class AdvancedSslSocketFactory implements ProtocolSocketFactory {
-
-    private static final String TAG = AdvancedSslSocketFactory.class.getSimpleName();
-    
-    private SSLContext mSslContext = null;
-    private AdvancedX509TrustManager mTrustManager = null;
-    private X509HostnameVerifier mHostnameVerifier = null;
-
-    public SSLContext getSslContext() {
-        return mSslContext;
-    }
-    
-    /**
-     * Constructor for AdvancedSSLProtocolSocketFactory.
-     */
-    public AdvancedSslSocketFactory(SSLContext sslContext, AdvancedX509TrustManager trustManager, X509HostnameVerifier hostnameVerifier) {
-        if (sslContext == null)
-            throw new IllegalArgumentException("AdvancedSslSocketFactory can not be created with a null SSLContext");
-        if (trustManager == null)
-            throw new IllegalArgumentException("AdvancedSslSocketFactory can not be created with a null Trust Manager");
-        mSslContext = sslContext;
-        mTrustManager = trustManager;
-        mHostnameVerifier = hostnameVerifier;
-    }
-
-    /**
-     * @see ProtocolSocketFactory#createSocket(java.lang.String,int,java.net.InetAddress,int)
-     */
-    public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException, UnknownHostException {
-        Socket socket = mSslContext.getSocketFactory().createSocket(host, port, clientHost, clientPort);
-        verifyPeerIdentity(host, port, socket);
-        return socket;
-    }
-
-    
-    /**
-     * Attempts to get a new socket connection to the given host within the
-     * given time limit.
-     * 
-     * @param host the host name/IP
-     * @param port the port on the host
-     * @param clientHost the local host name/IP to bind the socket to
-     * @param clientPort the port on the local machine
-     * @param params {@link HttpConnectionParams Http connection parameters}
-     * 
-     * @return Socket a new socket
-     * 
-     * @throws IOException if an I/O error occurs while creating the socket
-     * @throws UnknownHostException if the IP address of the host cannot be
-     *             determined
-     */
-    public Socket createSocket(final String host, final int port,
-            final InetAddress localAddress, final int localPort,
-            final HttpConnectionParams params) throws IOException,
-            UnknownHostException, ConnectTimeoutException {
-        Log_OC.d(TAG, "Creating SSL Socket with remote " + host + ":" + port + ", local " + localAddress + ":" + localPort + ", params: " + params);
-        if (params == null) {
-            throw new IllegalArgumentException("Parameters may not be null");
-        } 
-        int timeout = params.getConnectionTimeout();
-        SocketFactory socketfactory = mSslContext.getSocketFactory();
-        Log_OC.d(TAG, " ... with connection timeout " + timeout + " and socket timeout " + params.getSoTimeout());
-        Socket socket = socketfactory.createSocket();
-        SocketAddress localaddr = new InetSocketAddress(localAddress, localPort);
-        SocketAddress remoteaddr = new InetSocketAddress(host, port);
-        socket.setSoTimeout(params.getSoTimeout());
-        socket.bind(localaddr);
-        socket.connect(remoteaddr, timeout);
-        verifyPeerIdentity(host, port, socket);
-        return socket;
-    }
-
-    /**
-     * @see ProtocolSocketFactory#createSocket(java.lang.String,int)
-     */
-    public Socket createSocket(String host, int port) throws IOException,
-            UnknownHostException {
-        Log_OC.d(TAG, "Creating SSL Socket with remote " + host + ":" + port);
-        Socket socket = mSslContext.getSocketFactory().createSocket(host, port);
-        verifyPeerIdentity(host, port, socket);
-        return socket; 
-    }
-
-    public boolean equals(Object obj) {
-        return ((obj != null) && obj.getClass().equals(
-                AdvancedSslSocketFactory.class));
-    }
-
-    public int hashCode() {
-        return AdvancedSslSocketFactory.class.hashCode();
-    }
-
-
-    public X509HostnameVerifier getHostNameVerifier() {
-        return mHostnameVerifier;
-    }
-    
-    
-    public void setHostNameVerifier(X509HostnameVerifier hostnameVerifier) {
-        mHostnameVerifier = hostnameVerifier;
-    }
-    
-    /**
-     * Verifies the identity of the server. 
-     * 
-     * The server certificate is verified first.
-     * 
-     * Then, the host name is compared with the content of the server certificate using the current host name verifier, if any.
-     * @param socket
-     */
-    private void verifyPeerIdentity(String host, int port, Socket socket) throws IOException {
-        try {
-            CertificateCombinedException failInHandshake = null;
-            /// 1. VERIFY THE SERVER CERTIFICATE through the registered TrustManager (that should be an instance of AdvancedX509TrustManager) 
-            try {
-                SSLSocket sock = (SSLSocket) socket;    // a new SSLSession instance is created as a "side effect" 
-                sock.startHandshake();
-                
-            } catch (RuntimeException e) {
-                
-                if (e instanceof CertificateCombinedException) {
-                    failInHandshake = (CertificateCombinedException) e;
-                } else {
-                    Throwable cause = e.getCause();
-                    Throwable previousCause = null;
-                    while (cause != null && cause != previousCause && !(cause instanceof CertificateCombinedException)) {
-                        previousCause = cause;
-                        cause = cause.getCause();
-                    }
-                    if (cause != null && cause instanceof CertificateCombinedException) {
-                        failInHandshake = (CertificateCombinedException)cause;
-                    }
-                }
-                if (failInHandshake == null) {
-                    throw e;
-                }
-                failInHandshake.setHostInUrl(host);
-                
-            }
-            
-            /// 2. VERIFY HOSTNAME
-            SSLSession newSession = null;
-            boolean verifiedHostname = true;
-            if (mHostnameVerifier != null) {
-                if (failInHandshake != null) {
-                    /// 2.1 : a new SSLSession instance was NOT created in the handshake
-                    X509Certificate serverCert = failInHandshake.getServerCertificate();
-                    try {
-                        mHostnameVerifier.verify(host, serverCert);
-                    } catch (SSLException e) {
-                        verifiedHostname = false;
-                    }
-                
-                } else {
-                    /// 2.2 : a new SSLSession instance was created in the handshake
-                    newSession = ((SSLSocket)socket).getSession();
-                    if (!mTrustManager.isKnownServer((X509Certificate)(newSession.getPeerCertificates()[0]))) {
-                        verifiedHostname = mHostnameVerifier.verify(host, newSession); 
-                    }
-                }
-            }
-
-            /// 3. Combine the exceptions to throw, if any
-            if (!verifiedHostname) {
-                SSLPeerUnverifiedException pue = new SSLPeerUnverifiedException("Names in the server certificate do not match to " + host + " in the URL");
-                if (failInHandshake == null) {
-                    failInHandshake = new CertificateCombinedException((X509Certificate) newSession.getPeerCertificates()[0]);
-                    failInHandshake.setHostInUrl(host);
-                }
-                failInHandshake.setSslPeerUnverifiedException(pue);
-                pue.initCause(failInHandshake);
-                throw pue;
-                
-            } else if (failInHandshake != null) {
-                SSLHandshakeException hse = new SSLHandshakeException("Server certificate could not be verified");
-                hse.initCause(failInHandshake);
-                throw hse;
-            }
-            
-        } catch (IOException io) {        
-            try {
-                socket.close();
-            } catch (Exception x) {
-                // NOTHING - irrelevant exception for the caller 
-            }
-            throw io;
-        }
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/network/AdvancedX509TrustManager.java b/src/de/mobilcom/debitel/cloud/android/network/AdvancedX509TrustManager.java
deleted file mode 100644 (file)
index 038ca39..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.network;
-
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertPathValidatorException;
-import java.security.cert.CertStoreException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
-import java.security.cert.X509Certificate;
-
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
-import javax.net.ssl.X509TrustManager;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-
-/**
- * @author David A. Velasco
- */
-public class AdvancedX509TrustManager implements X509TrustManager {
-    
-    private static final String TAG = AdvancedX509TrustManager.class.getSimpleName();
-
-    private X509TrustManager mStandardTrustManager = null;
-    private KeyStore mKnownServersKeyStore;
-
-    /**
-     * Constructor for AdvancedX509TrustManager
-     * 
-     * @param  knownServersCertStore    Local certificates store with server certificates explicitly trusted by the user.
-     * @throws CertStoreException       When no default X509TrustManager instance was found in the system.
-     */
-    public AdvancedX509TrustManager(KeyStore knownServersKeyStore)
-            throws NoSuchAlgorithmException, KeyStoreException, CertStoreException {
-        super();
-        TrustManagerFactory factory = TrustManagerFactory
-                .getInstance(TrustManagerFactory.getDefaultAlgorithm());
-        factory.init((KeyStore)null);
-        mStandardTrustManager = findX509TrustManager(factory);
-
-        mKnownServersKeyStore = knownServersKeyStore;
-    }
-    
-    
-    /**
-     * Locates the first X509TrustManager provided by a given TrustManagerFactory
-     * @param factory               TrustManagerFactory to inspect in the search for a X509TrustManager
-     * @return                      The first X509TrustManager found in factory.
-     * @throws CertStoreException   When no X509TrustManager instance was found in factory
-     */
-    private X509TrustManager findX509TrustManager(TrustManagerFactory factory) throws CertStoreException {
-        TrustManager tms[] = factory.getTrustManagers();
-        for (int i = 0; i < tms.length; i++) {
-            if (tms[i] instanceof X509TrustManager) {
-                return (X509TrustManager) tms[i];
-            }
-        }
-        return null;
-    }
-    
-
-    /**
-     * @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[],
-     *      String authType)
-     */
-    public void checkClientTrusted(X509Certificate[] certificates, String authType) throws CertificateException {
-        mStandardTrustManager.checkClientTrusted(certificates, authType);
-    }
-
-    
-    /**
-     * @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[],
-     *      String authType)
-     */
-    public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException {
-        if (!isKnownServer(certificates[0])) {
-               CertificateCombinedException result = new CertificateCombinedException(certificates[0]);
-               try {
-                       certificates[0].checkValidity();
-               } catch (CertificateExpiredException c) {
-                       result.setCertificateExpiredException(c);
-                       
-               } catch (CertificateNotYetValidException c) {
-                result.setCertificateNotYetException(c);
-               }
-               
-               try {
-                   mStandardTrustManager.checkServerTrusted(certificates, authType);
-               } catch (CertificateException c) {
-                Throwable cause = c.getCause();
-                Throwable previousCause = null;
-                while (cause != null && cause != previousCause && !(cause instanceof CertPathValidatorException)) {     // getCause() is not funny
-                    previousCause = cause;
-                    cause = cause.getCause();
-                }
-                if (cause != null && cause instanceof CertPathValidatorException) {
-                       result.setCertPathValidatorException((CertPathValidatorException)cause);
-                } else {
-                       result.setOtherCertificateException(c);
-                }
-               }
-               
-               if (result.isException())
-                       throw result;
-
-        }
-    }
-    
-    
-    /**
-     * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()
-     */
-    public X509Certificate[] getAcceptedIssuers() {
-        return mStandardTrustManager.getAcceptedIssuers();
-    }
-
-    
-    public boolean isKnownServer(X509Certificate cert) {
-        try {
-            return (mKnownServersKeyStore.getCertificateAlias(cert) != null);
-        } catch (KeyStoreException e) {
-            Log_OC.d(TAG, "Fail while checking certificate in the known-servers store");
-            return false;
-        }
-    }
-    
-}
\ No newline at end of file
diff --git a/src/de/mobilcom/debitel/cloud/android/network/BearerAuthScheme.java b/src/de/mobilcom/debitel/cloud/android/network/BearerAuthScheme.java
deleted file mode 100644 (file)
index 3d4abc0..0000000
+++ /dev/null
@@ -1,268 +0,0 @@
-/* ownCloud Android client application
- *   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 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 de.mobilcom.debitel.cloud.android.network;
-
-import java.util.Map;
-
-import org.apache.commons.httpclient.Credentials;
-import org.apache.commons.httpclient.HttpMethod;
-import org.apache.commons.httpclient.auth.AuthChallengeParser;
-import org.apache.commons.httpclient.auth.AuthScheme;
-import org.apache.commons.httpclient.auth.AuthenticationException;
-import org.apache.commons.httpclient.auth.InvalidCredentialsException;
-import org.apache.commons.httpclient.auth.MalformedChallengeException;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-
-/**
- * Bearer authentication scheme as defined in RFC 6750.
- * 
- * @author David A. Velasco
- */
-
-public class BearerAuthScheme implements AuthScheme /*extends RFC2617Scheme*/ {
-    
-    private static final String TAG = BearerAuthScheme.class.getSimpleName();
-
-    public static final String AUTH_POLICY = "Bearer";
-    
-    /** Whether the bearer authentication process is complete */
-    private boolean mComplete;
-    
-    /** Authentication parameter map */
-    private Map mParams = null;
-    
-    
-    /**
-     * Default constructor for the bearer authentication scheme.
-     */
-    public BearerAuthScheme() {
-        mComplete = false;
-    }
-
-    /**
-     * Constructor for the basic authentication scheme.
-     * 
-     * @param   challenge                       Authentication challenge
-     * 
-     * @throws  MalformedChallengeException     Thrown if the authentication challenge is malformed
-     * 
-     * @deprecated Use parameterless constructor and {@link AuthScheme#processChallenge(String)} method
-     */
-    public BearerAuthScheme(final String challenge) throws MalformedChallengeException {
-        processChallenge(challenge);
-        mComplete = true;
-    }
-
-    /**
-     * Returns textual designation of the bearer authentication scheme.
-     * 
-     * @return "Bearer"
-     */
-    public String getSchemeName() {
-        return "bearer";
-    }
-
-    /**
-     * Processes the Bearer challenge.
-     *  
-     * @param   challenge                   The challenge string
-     * 
-     * @throws MalformedChallengeException  Thrown if the authentication challenge is malformed
-     */
-    public void processChallenge(String challenge) throws MalformedChallengeException {
-        String s = AuthChallengeParser.extractScheme(challenge);
-        if (!s.equalsIgnoreCase(getSchemeName())) {
-            throw new MalformedChallengeException(
-              "Invalid " + getSchemeName() + " challenge: " + challenge); 
-        }
-        mParams = AuthChallengeParser.extractParams(challenge);
-        mComplete = true;
-    }
-
-    /**
-     * Tests if the Bearer authentication process has been completed.
-     * 
-     * @return 'true' if Bearer authorization has been processed, 'false' otherwise.
-     */
-    public boolean isComplete() {
-        return this.mComplete;
-    }
-
-    /**
-     * Produces bearer authorization string for the given set of 
-     * {@link Credentials}.
-     * 
-     * @param   credentials                     The set of credentials to be used for authentication
-     * @param   method                          Method name is ignored by the bearer authentication scheme
-     * @param   uri                             URI is ignored by the bearer authentication scheme
-     * @throws  InvalidCredentialsException     If authentication credentials are not valid or not applicable 
-     *                                          for this authentication scheme
-     * @throws  AuthenticationException         If authorization string cannot be generated due to an authentication failure
-     * @return  A bearer authorization string
-     * 
-     * @deprecated Use {@link #authenticate(Credentials, HttpMethod)}
-     */
-    public String authenticate(Credentials credentials, String method, String uri) throws AuthenticationException {
-        Log_OC.d(TAG, "enter BearerScheme.authenticate(Credentials, String, String)");
-
-        BearerCredentials bearer = null;
-        try {
-            bearer = (BearerCredentials) credentials;
-        } catch (ClassCastException e) {
-            throw new InvalidCredentialsException(
-             "Credentials cannot be used for bearer authentication: " 
-              + credentials.getClass().getName());
-        }
-        return BearerAuthScheme.authenticate(bearer);
-    }
-
-    
-    /**
-     * Returns 'false'. Bearer authentication scheme is request based.
-     * 
-     * @return 'false'.
-     */
-    public boolean isConnectionBased() {
-        return false;    
-    }
-
-    /**
-     * Produces bearer authorization string for the given set of {@link Credentials}.
-     * 
-     * @param   credentials                     The set of credentials to be used for authentication
-     * @param   method                          The method being authenticated
-     * @throws  InvalidCredentialsException     If authentication credentials are not valid or not applicable for this authentication 
-     *                                          scheme.
-     * @throws AuthenticationException         If authorization string cannot be generated due to an authentication failure.
-     * 
-     * @return a basic authorization string
-     */
-    public String authenticate(Credentials credentials, HttpMethod method) throws AuthenticationException {
-        Log_OC.d(TAG, "enter BearerScheme.authenticate(Credentials, HttpMethod)");
-
-        if (method == null) {
-            throw new IllegalArgumentException("Method may not be null");
-        }
-        BearerCredentials bearer = null;
-        try {
-            bearer = (BearerCredentials) credentials;
-        } catch (ClassCastException e) {
-            throw new InvalidCredentialsException(
-                    "Credentials cannot be used for bearer authentication: " 
-                    + credentials.getClass().getName());
-        }
-        return BearerAuthScheme.authenticate(
-            bearer, 
-            method.getParams().getCredentialCharset());
-    }
-    
-    /**
-     * @deprecated Use {@link #authenticate(BearerCredentials, String)}
-     * 
-     * Returns a bearer Authorization header value for the given 
-     * {@link BearerCredentials}.
-     * 
-     * @param   credentials     The credentials to encode.
-     * 
-     * @return                  A bearer authorization string
-     */
-    public static String authenticate(BearerCredentials credentials) {
-        return authenticate(credentials, "ISO-8859-1");
-    }
-
-    /**
-     * Returns a bearer Authorization header value for the given 
-     * {@link BearerCredentials} and charset.
-     * 
-     * @param   credentials         The credentials to encode.
-     * @param   charset             The charset to use for encoding the credentials
-     * 
-     * @return                      A bearer authorization string
-     * 
-     * @since 3.0
-     */
-    public static String authenticate(BearerCredentials credentials, String charset) {
-        Log_OC.d(TAG, "enter BearerAuthScheme.authenticate(BearerCredentials, String)");
-
-        if (credentials == null) {
-            throw new IllegalArgumentException("Credentials may not be null"); 
-        }
-        if (charset == null || charset.length() == 0) {
-            throw new IllegalArgumentException("charset may not be null or empty");
-        }
-        StringBuffer buffer = new StringBuffer();
-        buffer.append(credentials.getAccessToken());
-        
-        //return "Bearer " + EncodingUtil.getAsciiString(EncodingUtil.getBytes(buffer.toString(), charset));
-        return "Bearer " + buffer.toString();
-    }
-
-    /**
-     * Returns a String identifying the authentication challenge.  This is
-     * used, in combination with the host and port to determine if
-     * authorization has already been attempted or not.  Schemes which
-     * require multiple requests to complete the authentication should
-     * return a different value for each stage in the request.
-     * 
-     * Additionally, the ID should take into account any changes to the
-     * authentication challenge and return a different value when appropriate.
-     * For example when the realm changes in basic authentication it should be
-     * considered a different authentication attempt and a different value should
-     * be returned.
-     * 
-     * This method simply returns the realm for the challenge.
-     * 
-     * @return String       a String identifying the authentication challenge.
-     * 
-     * @deprecated no longer used
-     */
-    @Override
-    public String getID() {
-        return getRealm();
-    }
-
-    /**
-     * Returns authentication parameter with the given name, if available.
-     * 
-     * @param   name    The name of the parameter to be returned
-     * 
-     * @return          The parameter with the given name
-     */
-    @Override
-    public String getParameter(String name) {
-        if (name == null) {
-            throw new IllegalArgumentException("Parameter name may not be null"); 
-        }
-        if (mParams == null) {
-            return null;
-        }
-        return (String) mParams.get(name.toLowerCase());
-    }
-
-    /**
-     * Returns authentication realm. The realm may not be null.
-     * 
-     * @return  The authentication realm
-     */
-    @Override
-    public String getRealm() {
-        return getParameter("realm");
-    }
-    
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/network/BearerCredentials.java b/src/de/mobilcom/debitel/cloud/android/network/BearerCredentials.java
deleted file mode 100644 (file)
index 12e62e9..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/* ownCloud Android client application
- *   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 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 de.mobilcom.debitel.cloud.android.network;
-
-import org.apache.commons.httpclient.Credentials;
-import org.apache.commons.httpclient.util.LangUtils;
-
-/**
- * Bearer token {@link Credentials}
- *
- * @author David A. Velasco
- */
-public class BearerCredentials implements Credentials {
-
-    
-    private String mAccessToken;
-    
-    
-    /**
-     * The constructor with the bearer token
-     *
-     * @param token     The bearer token
-     */
-    public BearerCredentials(String token) {
-        /*if (token == null) {
-            throw new IllegalArgumentException("Bearer token may not be null");            
-        }*/
-        mAccessToken = (token == null) ? "" : token;
-    }
-
-
-    /**
-     * Returns the access token
-     *
-     * @return      The access token
-     */
-    public String getAccessToken() {
-        return mAccessToken;
-    }
-
-
-    /**
-     * Get this object string.
-     *
-     * @return  The access token
-     */
-    public String toString() {
-        return mAccessToken;
-    }
-
-    /**
-     * Does a hash of the access token.
-     *
-     * @return The hash code of the access token
-     */
-    public int hashCode() {
-        int hash = LangUtils.HASH_SEED;
-        hash = LangUtils.hashCode(hash, mAccessToken);
-        return hash;
-    }
-
-    /**
-     * These credentials are assumed equal if accessToken is the same.
-     *
-     * @param   o   The other object to compare with.
-     *
-     * @return      'True' if the object is equivalent.
-     */
-    public boolean equals(Object o) {
-        if (o == null) return false;
-        if (this == o) return true;
-        if (this.getClass().equals(o.getClass())) {
-            BearerCredentials that = (BearerCredentials) o;
-            if (LangUtils.equals(mAccessToken, that.mAccessToken)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-}
-
diff --git a/src/de/mobilcom/debitel/cloud/android/network/CertificateCombinedException.java b/src/de/mobilcom/debitel/cloud/android/network/CertificateCombinedException.java
deleted file mode 100644 (file)
index d767ea0..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.network;
-
-import java.security.cert.CertPathValidatorException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
-import java.security.cert.X509Certificate;
-
-import javax.net.ssl.SSLPeerUnverifiedException;
-
-/**
- * Exception joining all the problems that {@link AdvancedX509TrustManager} can find in
- * a certificate chain for a server.
- * 
- * This was initially created as an extension of CertificateException, but some
- * implementations of the SSL socket layer in existing devices are REPLACING the CertificateException
- * instances thrown by {@link javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[], String)}
- * with SSLPeerUnverifiedException FORGETTING THE CAUSING EXCEPTION instead of wrapping it. 
- * 
- * Due to this, extending RuntimeException is necessary to get that the CertificateCombinedException 
- * instance reaches {@link AdvancedSslSocketFactory#verifyPeerIdentity}.
- * 
- * BE CAREFUL. As a RuntimeException extensions, Java compilers do not require to handle it
- * in client methods. Be sure to use it only when you know exactly where it will go.
- * 
- * @author David A. Velasco
- */
-public class CertificateCombinedException extends RuntimeException {
-
-    /** Generated - to refresh every time the class changes */
-    private static final long serialVersionUID = -8875782030758554999L;
-    
-    private X509Certificate mServerCert = null;
-    private String mHostInUrl;
-
-    private CertificateExpiredException mCertificateExpiredException = null;
-    private CertificateNotYetValidException mCertificateNotYetValidException = null;
-    private CertPathValidatorException mCertPathValidatorException = null;
-    private CertificateException mOtherCertificateException = null;
-    private SSLPeerUnverifiedException mSslPeerUnverifiedException = null;
-    
-    public CertificateCombinedException(X509Certificate x509Certificate) {
-        mServerCert = x509Certificate;
-    }
-
-    public X509Certificate getServerCertificate() {
-        return mServerCert;
-    }
-
-    public String getHostInUrl() {
-        return mHostInUrl;
-    }
-
-    public void setHostInUrl(String host) {
-        mHostInUrl = host;
-    }
-
-    public CertificateExpiredException getCertificateExpiredException() {
-        return mCertificateExpiredException;
-    }
-
-    public void setCertificateExpiredException(CertificateExpiredException c) {
-        mCertificateExpiredException  = c;
-    }
-
-    public CertificateNotYetValidException getCertificateNotYetValidException() {
-        return mCertificateNotYetValidException;
-    }
-
-    public void setCertificateNotYetException(CertificateNotYetValidException c) {
-        mCertificateNotYetValidException = c;
-    }
-
-    public CertPathValidatorException getCertPathValidatorException() {
-        return mCertPathValidatorException;
-    }
-
-    public void setCertPathValidatorException(CertPathValidatorException c) {
-        mCertPathValidatorException = c;
-    }
-
-    public CertificateException getOtherCertificateException() {
-        return mOtherCertificateException;
-    }
-
-    public void setOtherCertificateException(CertificateException c) {
-        mOtherCertificateException = c;
-    }
-
-    public SSLPeerUnverifiedException getSslPeerUnverifiedException() {
-        return mSslPeerUnverifiedException ; 
-    }
-
-    public void setSslPeerUnverifiedException(SSLPeerUnverifiedException s) {
-        mSslPeerUnverifiedException = s;
-    }
-
-    public boolean isException() {
-        return (mCertificateExpiredException != null ||
-                mCertificateNotYetValidException != null ||
-                mCertPathValidatorException != null ||
-                mOtherCertificateException != null ||
-                mSslPeerUnverifiedException != null);
-    }
-
-    public boolean isRecoverable() {
-        return (mCertificateExpiredException != null ||
-                mCertificateNotYetValidException != null ||
-                mCertPathValidatorException != null ||
-                mSslPeerUnverifiedException != null);
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/network/OwnCloudClientUtils.java b/src/de/mobilcom/debitel/cloud/android/network/OwnCloudClientUtils.java
deleted file mode 100644 (file)
index 767d9bb..0000000
+++ /dev/null
@@ -1,284 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.network;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.GeneralSecurityException;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
-
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.TrustManager;
-
-import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
-import org.apache.commons.httpclient.protocol.Protocol;
-import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
-import org.apache.http.conn.ssl.X509HostnameVerifier;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.authentication.AccountAuthenticator;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils.AccountNotFoundException;
-
-import eu.alefzero.webdav.WebdavClient;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.AccountManagerFuture;
-import android.accounts.AuthenticatorException;
-import android.accounts.OperationCanceledException;
-import android.app.Activity;
-import android.content.Context;
-import android.net.Uri;
-import android.os.Bundle;
-
-public class OwnCloudClientUtils {
-    
-    final private static String TAG = OwnCloudClientUtils.class.getSimpleName();
-    
-    /** Default timeout for waiting data from the server */
-    public static final int DEFAULT_DATA_TIMEOUT = 60000;
-    
-    /** Default timeout for establishing a connection */
-    public static final int DEFAULT_CONNECTION_TIMEOUT = 60000;
-
-    /** Connection manager for all the WebdavClients */
-    private static MultiThreadedHttpConnectionManager mConnManager = null;
-    
-    private static Protocol mDefaultHttpsProtocol = null;
-
-    private static AdvancedSslSocketFactory mAdvancedSslSocketFactory = null;
-
-    private static X509HostnameVerifier mHostnameVerifier = null;
-    
-    
-    /**
-     * Creates a WebdavClient setup for an ownCloud account
-     * 
-     * Do not call this method from the main thread.
-     * 
-     * @param account                       The ownCloud account
-     * @param appContext                    Android application context
-     * @return                              A WebdavClient object ready to be used
-     * @throws AuthenticatorException       If the authenticator failed to get the authorization token for the account.
-     * @throws OperationCanceledException   If the authenticator operation was cancelled while getting the authorization token for the account. 
-     * @throws IOException                  If there was some I/O error while getting the authorization token for the account.
-     * @throws AccountNotFoundException     If 'account' is unknown for the AccountManager
-     */
-    public static WebdavClient createOwnCloudClient (Account account, Context appContext) throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException {
-        //Log_OC.d(TAG, "Creating WebdavClient associated to " + account.name);
-       
-        Uri uri = Uri.parse(AccountUtils.constructFullURLForAccount(appContext, account));
-        AccountManager am = AccountManager.get(appContext);
-        boolean isOauth2 = am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null;   // TODO avoid calling to getUserData here
-        boolean isSamlSso = am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO) != null;
-        WebdavClient client = createOwnCloudClient(uri, appContext, !isSamlSso);
-        if (isOauth2) {    
-            String accessToken = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypeAccessToken(), false);
-            client.setBearerCredentials(accessToken);   // TODO not assume that the access token is a bearer token
-        
-        } else if (isSamlSso) {    // TODO avoid a call to getUserData here
-            String accessToken = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypeSamlSessionCookie(), false);
-            client.setSsoSessionCookie(accessToken);
-            
-        } else {
-            String username = account.name.substring(0, account.name.lastIndexOf('@'));
-            //String password = am.getPassword(account);
-            String password = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypePass(), false);
-            client.setBasicCredentials(username, password);
-        }
-        
-        return client;
-    }
-    
-    
-    public static WebdavClient createOwnCloudClient (Account account, Context appContext, Activity currentActivity) throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException {
-        Uri uri = Uri.parse(AccountUtils.constructFullURLForAccount(appContext, account));
-        AccountManager am = AccountManager.get(appContext);
-        boolean isOauth2 = am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null;   // TODO avoid calling to getUserData here
-        boolean isSamlSso = am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO) != null;
-        WebdavClient client = createOwnCloudClient(uri, appContext, !isSamlSso);
-        
-        if (isOauth2) {    // TODO avoid a call to getUserData here
-            AccountManagerFuture<Bundle> future =  am.getAuthToken(account, MainApp.getAuthTokenTypeAccessToken(), null, currentActivity, null, null);
-            Bundle result = future.getResult();
-            String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN);
-            if (accessToken == null) throw new AuthenticatorException("WTF!");
-            client.setBearerCredentials(accessToken);   // TODO not assume that the access token is a bearer token
-
-        } else if (isSamlSso) {    // TODO avoid a call to getUserData here
-            AccountManagerFuture<Bundle> future =  am.getAuthToken(account, MainApp.getAuthTokenTypeSamlSessionCookie(), null, currentActivity, null, null);
-            Bundle result = future.getResult();
-            String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN);
-            if (accessToken == null) throw new AuthenticatorException("WTF!");
-            client.setSsoSessionCookie(accessToken);
-
-        } else {
-            String username = account.name.substring(0, account.name.lastIndexOf('@'));
-            //String password = am.getPassword(account);
-            //String password = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypePass(), false);
-            AccountManagerFuture<Bundle> future =  am.getAuthToken(account, MainApp.getAuthTokenTypePass(), null, currentActivity, null, null);
-            Bundle result = future.getResult();
-            String password = result.getString(AccountManager.KEY_AUTHTOKEN);
-            client.setBasicCredentials(username, password);
-        }
-        
-        return client;
-    }
-    
-    /**
-     * Creates a WebdavClient to access a URL and sets the desired parameters for ownCloud client connections.
-     * 
-     * @param uri       URL to the ownCloud server
-     * @param context   Android context where the WebdavClient is being created.
-     * @return          A WebdavClient object ready to be used
-     */
-    public static WebdavClient createOwnCloudClient(Uri uri, Context context, boolean followRedirects) {
-        try {
-            registerAdvancedSslContext(true, context);
-        }  catch (GeneralSecurityException 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_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());
-        
-        client.setDefaultTimeouts(DEFAULT_DATA_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT);
-        client.setBaseUri(uri);
-        client.setFollowRedirects(followRedirects);
-        
-        return client;
-    }
-    
-    
-    /**
-     * Registers or unregisters the proper components for advanced SSL handling.
-     * @throws IOException 
-     */
-    private static void registerAdvancedSslContext(boolean register, Context context) throws GeneralSecurityException, IOException {
-        Protocol pr = null;
-        try {
-            pr = Protocol.getProtocol("https");
-            if (pr != null && mDefaultHttpsProtocol == null) {
-                mDefaultHttpsProtocol = pr;
-            }
-        } catch (IllegalStateException e) {
-            // nothing to do here; really
-        }
-        boolean isRegistered = (pr != null && pr.getSocketFactory() instanceof AdvancedSslSocketFactory);
-        if (register && !isRegistered) {
-            Protocol.registerProtocol("https", new Protocol("https", getAdvancedSslSocketFactory(context), 443));
-            
-        } else if (!register && isRegistered) {
-            if (mDefaultHttpsProtocol != null) {
-                Protocol.registerProtocol("https", mDefaultHttpsProtocol);
-            }
-        }
-    }
-    
-    public static AdvancedSslSocketFactory getAdvancedSslSocketFactory(Context context) throws GeneralSecurityException, IOException {
-        if (mAdvancedSslSocketFactory  == null) {
-            KeyStore trustStore = getKnownServersStore(context);
-            AdvancedX509TrustManager trustMgr = new AdvancedX509TrustManager(trustStore);
-            TrustManager[] tms = new TrustManager[] { trustMgr };
-                
-            SSLContext sslContext = SSLContext.getInstance("TLS");
-            sslContext.init(null, tms, null);
-                    
-            mHostnameVerifier = new BrowserCompatHostnameVerifier();
-            mAdvancedSslSocketFactory = new AdvancedSslSocketFactory(sslContext, trustMgr, mHostnameVerifier);
-        }
-        return mAdvancedSslSocketFactory;
-    }
-
-
-    private static String LOCAL_TRUSTSTORE_FILENAME = "knownServers.bks";
-    
-    private static String LOCAL_TRUSTSTORE_PASSWORD = "password";
-
-    private static KeyStore mKnownServersStore = null;
-    
-    /**
-     * Returns the local store of reliable server certificates, explicitly accepted by the user.
-     * 
-     * Returns a KeyStore instance with empty content if the local store was never created.
-     * 
-     * Loads the store from the storage environment if needed.
-     * 
-     * @param context                       Android context where the operation is being performed.
-     * @return                              KeyStore instance with explicitly-accepted server certificates. 
-     * @throws KeyStoreException            When the KeyStore instance could not be created.
-     * @throws IOException                  When an existing local trust store could not be loaded.
-     * @throws NoSuchAlgorithmException     When the existing local trust store was saved with an unsupported algorithm.
-     * @throws CertificateException         When an exception occurred while loading the certificates from the local trust store.
-     */
-    private static KeyStore getKnownServersStore(Context context) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
-        if (mKnownServersStore == null) {
-            //mKnownServersStore = KeyStore.getInstance("BKS");
-            mKnownServersStore = KeyStore.getInstance(KeyStore.getDefaultType());
-            File localTrustStoreFile = new File(context.getFilesDir(), LOCAL_TRUSTSTORE_FILENAME);
-            Log_OC.d(TAG, "Searching known-servers store at " + localTrustStoreFile.getAbsolutePath());
-            if (localTrustStoreFile.exists()) {
-                InputStream in = new FileInputStream(localTrustStoreFile);
-                try {
-                    mKnownServersStore.load(in, LOCAL_TRUSTSTORE_PASSWORD.toCharArray());
-                } finally {
-                    in.close();
-                }
-            } else {
-                mKnownServersStore.load(null, LOCAL_TRUSTSTORE_PASSWORD.toCharArray()); // necessary to initialize an empty KeyStore instance
-            }
-        }
-        return mKnownServersStore;
-    }
-    
-    
-    public static void addCertToKnownServersStore(Certificate cert, Context context) throws  KeyStoreException, NoSuchAlgorithmException, 
-                                                                                            CertificateException, IOException {
-        KeyStore knownServers = getKnownServersStore(context);
-        knownServers.setCertificateEntry(Integer.toString(cert.hashCode()), cert);
-        FileOutputStream fos = null;
-        try {
-            fos = context.openFileOutput(LOCAL_TRUSTSTORE_FILENAME, Context.MODE_PRIVATE);
-            knownServers.store(fos, LOCAL_TRUSTSTORE_PASSWORD.toCharArray());
-        } finally {
-            fos.close();
-        }
-    }
-    
-    
-    static private MultiThreadedHttpConnectionManager getMultiThreadedConnManager() {
-        if (mConnManager == null) {
-            mConnManager = new MultiThreadedHttpConnectionManager();
-            mConnManager.getParams().setDefaultMaxConnectionsPerHost(5);
-            mConnManager.getParams().setMaxTotalConnections(5);
-        }
-        return mConnManager;
-    }
-
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/network/ProgressiveDataTransferer.java b/src/de/mobilcom/debitel/cloud/android/network/ProgressiveDataTransferer.java
deleted file mode 100644 (file)
index f066c8a..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.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);
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/operations/ChunkedUploadFileOperation.java b/src/de/mobilcom/debitel/cloud/android/operations/ChunkedUploadFileOperation.java
deleted file mode 100644 (file)
index 3c30724..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.operations;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.nio.channels.FileChannel;
-import java.util.Random;
-
-import org.apache.commons.httpclient.HttpException;
-import org.apache.commons.httpclient.methods.PutMethod;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.network.ProgressiveDataTransferer;
-
-import android.accounts.Account;
-
-import eu.alefzero.webdav.ChunkFromFileChannelRequestEntity;
-import eu.alefzero.webdav.WebdavClient;
-import eu.alefzero.webdav.WebdavUtils;
-
-public class ChunkedUploadFileOperation extends UploadFileOperation {
-    
-    private static final long CHUNK_SIZE = 1024000;
-    private static final String OC_CHUNKED_HEADER = "OC-Chunked";
-    private static final String TAG = ChunkedUploadFileOperation.class.getSimpleName();
-
-    public ChunkedUploadFileOperation(  Account account,
-                                        OCFile file,
-                                        boolean isInstant, 
-                                        boolean forceOverwrite,
-                                        int localBehaviour) {
-        
-        super(account, file, isInstant, forceOverwrite, localBehaviour);
-    }
-
-    @Override
-    protected int uploadFile(WebdavClient client) throws HttpException, IOException {
-        int status = -1;
-
-        FileChannel channel = null;
-        RandomAccessFile raf = null;
-        try {
-            File file = new File(getStoragePath());
-            raf = new RandomAccessFile(file, "r");
-            channel = raf.getChannel();
-            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) {
-                if (mPutMethod != null) {
-                    mPutMethod.releaseConnection();    // let the connection available for other methods
-                }
-                mPutMethod = new PutMethod(uriPrefix + chunkCount + "-" + chunkIndex);
-                mPutMethod.addRequestHeader(OC_CHUNKED_HEADER, OC_CHUNKED_HEADER);
-                ((ChunkFromFileChannelRequestEntity)mEntity).setOffset(offset);
-                mPutMethod.setRequestEntity(mEntity);
-                status = client.executeMethod(mPutMethod);
-                client.exhaustResponse(mPutMethod.getResponseBodyAsStream());
-                Log_OC.d(TAG, "Upload of " + getStoragePath() + " to " + getRemotePath() + ", chunk index " + chunkIndex + ", count " + chunkCount + ", HTTP result status " + status);
-                if (!isSuccess(status))
-                    break;
-            }
-            
-        } finally {
-            if (channel != null)
-                channel.close();
-            if (raf != null)
-                raf.close();
-            if (mPutMethod != null)
-                mPutMethod.releaseConnection();    // let the connection available for other methods
-        }
-        return status;
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/operations/CreateFolderOperation.java b/src/de/mobilcom/debitel/cloud/android/operations/CreateFolderOperation.java
deleted file mode 100644 (file)
index f9bc65c..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-/* ownCloud Android client application
- *   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 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 de.mobilcom.debitel.cloud.android.operations;
-
-import java.io.File;
-
-import org.apache.commons.httpclient.HttpStatus;
-import org.apache.jackrabbit.webdav.client.methods.MkColMethod;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-
-import eu.alefzero.webdav.WebdavClient;
-import eu.alefzero.webdav.WebdavUtils;
-
-/**
- * Remote operation performing the creation of a new folder in the ownCloud server.
- * 
- * @author David A. Velasco 
- */
-public class CreateFolderOperation extends RemoteOperation {
-    
-    private static final String TAG = CreateFolderOperation.class.getSimpleName();
-
-    private static final int READ_TIMEOUT = 10000;
-    private static final int CONNECTION_TIMEOUT = 5000;
-    
-    protected String mRemotePath;
-    protected boolean mCreateFullPath;
-    protected DataStorageManager mStorageManager;
-    
-    /**
-     * Constructor
-     * 
-     * @param remotePath            Full path to the new directory to create in the remote server.
-     * @param createFullPath        'True' means that all the ancestor folders should be created if don't exist yet.
-     * @param storageManager        Reference to the local database corresponding to the account where the file is contained. 
-     */
-    public CreateFolderOperation(String remotePath, boolean createFullPath, DataStorageManager storageManager) {
-        mRemotePath = remotePath;
-        mCreateFullPath = createFullPath;
-        mStorageManager = storageManager;
-    }
-    
-    
-    /**
-     * Performs the operation
-     * 
-     * @param   client      Client object to communicate with the remote ownCloud server.
-     */
-    @Override
-    protected RemoteOperationResult run(WebdavClient client) {
-        RemoteOperationResult result = null;
-        MkColMethod mkcol = null;
-        try {
-            mkcol = new MkColMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath));
-            int status =  client.executeMethod(mkcol, READ_TIMEOUT, CONNECTION_TIMEOUT);
-            if (!mkcol.succeeded() && mkcol.getStatusCode() == HttpStatus.SC_CONFLICT && mCreateFullPath) {
-                result = createParentFolder(getParentPath(), client);
-                status = client.executeMethod(mkcol, READ_TIMEOUT, CONNECTION_TIMEOUT);    // second (and last) try
-            }
-            if (mkcol.succeeded()) {
-                // Save new directory in local database
-                OCFile newDir = new OCFile(mRemotePath);
-                newDir.setMimetype("DIR");
-                long parentId = mStorageManager.getFileByPath(getParentPath()).getFileId();
-                newDir.setParentId(parentId);
-                newDir.setModificationTimestamp(System.currentTimeMillis());
-                mStorageManager.saveFile(newDir);
-            }
-            result = new RemoteOperationResult(mkcol.succeeded(), status, mkcol.getResponseHeaders());
-            Log_OC.d(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage());
-            client.exhaustResponse(mkcol.getResponseBodyAsStream());
-                
-        } catch (Exception e) {
-            result = new RemoteOperationResult(e);
-            Log_OC.e(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage(), e);
-            
-        } finally {
-            if (mkcol != null)
-                mkcol.releaseConnection();
-        }
-        return result;
-    }
-
-
-    private String getParentPath() {
-        String parentPath = new File(mRemotePath).getParent();
-        parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR;
-        return parentPath;
-    }
-
-    
-    private RemoteOperationResult createParentFolder(String parentPath, WebdavClient client) {
-        RemoteOperation operation = new CreateFolderOperation(  parentPath,
-                                                                mCreateFullPath,
-                                                                mStorageManager    );
-        return operation.execute(client);
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/operations/DownloadFileOperation.java b/src/de/mobilcom/debitel/cloud/android/operations/DownloadFileOperation.java
deleted file mode 100644 (file)
index 7fcb52f..0000000
+++ /dev/null
@@ -1,235 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.operations;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.apache.commons.httpclient.Header;
-import org.apache.commons.httpclient.HttpException;
-import org.apache.commons.httpclient.methods.GetMethod;
-import org.apache.http.HttpStatus;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult;
-import de.mobilcom.debitel.cloud.android.utils.FileStorageUtils;
-
-import eu.alefzero.webdav.OnDatatransferProgressListener;
-import eu.alefzero.webdav.WebdavClient;
-import eu.alefzero.webdav.WebdavUtils;
-import android.accounts.Account;
-import android.webkit.MimeTypeMap;
-
-/**
- * Remote operation performing the download of a file to an ownCloud server
- * 
- * @author David A. Velasco
- */
-public class DownloadFileOperation extends RemoteOperation {
-    
-    private static final String TAG = DownloadFileOperation.class.getSimpleName();
-
-    private Account mAccount;
-    private OCFile mFile;
-    private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
-    private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
-    private long mModificationTimestamp = 0;
-    private GetMethod mGet;
-
-    
-    public DownloadFileOperation(Account account, OCFile file) {
-        if (account == null)
-            throw new IllegalArgumentException("Illegal null account in DownloadFileOperation creation");
-        if (file == null)
-            throw new IllegalArgumentException("Illegal null file in DownloadFileOperation creation");
-        
-        mAccount = account;
-        mFile = file;
-    }
-
-
-    public Account getAccount() {
-        return mAccount;
-    }
-    
-    public OCFile getFile() {
-        return mFile;
-    }
-
-    public String getSavePath() {
-        String path = mFile.getStoragePath();   // re-downloads should be done over the original file 
-        if (path != null && path.length() > 0) {
-            return path;
-        }
-        return FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile);
-    }
-    
-    public String getTmpPath() {
-        return FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath();
-    }
-    
-    public String getRemotePath() {
-        return mFile.getRemotePath();
-    }
-
-    public String getMimeType() {
-        String mimeType = mFile.getMimetype();
-        if (mimeType == null || mimeType.length() <= 0) {
-            try {
-                mimeType = MimeTypeMap.getSingleton()
-                    .getMimeTypeFromExtension(
-                            mFile.getRemotePath().substring(mFile.getRemotePath().lastIndexOf('.') + 1));
-            } catch (IndexOutOfBoundsException e) {
-                Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + mFile.getRemotePath());
-            }
-        }
-        if (mimeType == null) {
-            mimeType = "application/octet-stream";
-        }
-        return mimeType;
-    }
-    
-    public long getSize() {
-        return mFile.getFileLength();
-    }
-    
-    public long getModificationTimestamp() {
-        return (mModificationTimestamp > 0) ? mModificationTimestamp : mFile.getModificationTimestamp();
-    }
-    
-    
-    public void addDatatransferProgressListener (OnDatatransferProgressListener 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;
-        File newFile = null;
-        boolean moved = true;
-        
-        /// download will be performed to a temporal file, then moved to the final location
-        File tmpFile = new File(getTmpPath());
-        
-        /// perform the download
-        try {
-            tmpFile.getParentFile().mkdirs();
-            int status = downloadFile(client, tmpFile);
-            if (isSuccess(status)) {
-                newFile = new File(getSavePath());
-                newFile.getParentFile().mkdirs();
-                moved = tmpFile.renameTo(newFile);
-            }
-            if (!moved)
-                result = new RemoteOperationResult(RemoteOperationResult.ResultCode.LOCAL_STORAGE_NOT_MOVED);
-            else
-                result = new RemoteOperationResult(isSuccess(status), status, (mGet != null ? mGet.getResponseHeaders() : null));
-            Log_OC.i(TAG, "Download of " + mFile.getRemotePath() + " to " + getSavePath() + ": " + result.getLogMessage());
-            
-        } catch (Exception e) {
-            result = new RemoteOperationResult(e);
-            Log_OC.e(TAG, "Download of " + mFile.getRemotePath() + " to " + getSavePath() + ": " + result.getLogMessage(), e);
-        }
-        
-        return result;
-    }
-
-    
-    public boolean isSuccess(int status) {
-        return (status == HttpStatus.SC_OK);
-    }
-    
-    
-    protected int downloadFile(WebdavClient client, File targetFile) throws HttpException, IOException, OperationCancelledException {
-        int status = -1;
-        boolean savedFile = false;
-        mGet = new GetMethod(client.getBaseUri() + WebdavUtils.encodePath(mFile.getRemotePath()));
-        Iterator<OnDatatransferProgressListener> it = null;
-        
-        FileOutputStream fos = null;
-        try {
-            status = client.executeMethod(mGet);
-            if (isSuccess(status)) {
-                targetFile.createNewFile();
-                BufferedInputStream bis = new BufferedInputStream(mGet.getResponseBodyAsStream());
-                fos = new FileOutputStream(targetFile);
-                long transferred = 0;
-
-                byte[] bytes = new byte[4096];
-                int readResult = 0;
-                while ((readResult = bis.read(bytes)) != -1) {
-                    synchronized(mCancellationRequested) {
-                        if (mCancellationRequested.get()) {
-                            mGet.abort();
-                            throw new OperationCancelledException();
-                        }
-                    }
-                    fos.write(bytes, 0, readResult);
-                    transferred += readResult;
-                    synchronized (mDataTransferListeners) {
-                        it = mDataTransferListeners.iterator();
-                        while (it.hasNext()) {
-                            it.next().onTransferProgress(readResult, transferred, mFile.getFileLength(), targetFile.getName());
-                        }
-                    }
-                }
-                savedFile = true;
-                Header modificationTime = mGet.getResponseHeader("Last-Modified");
-                if (modificationTime != null) {
-                    Date d = WebdavUtils.parseResponseDate((String) modificationTime.getValue());
-                    mModificationTimestamp = (d != null) ? d.getTime() : 0;
-                }
-                
-            } else {
-                client.exhaustResponse(mGet.getResponseBodyAsStream());
-            }
-                
-        } finally {
-            if (fos != null) fos.close();
-            if (!savedFile && targetFile.exists()) {
-                targetFile.delete();
-            }
-            mGet.releaseConnection();    // let the connection available for other methods
-        }
-        return status;
-    }
-
-    
-    public void cancel() {
-        mCancellationRequested.set(true);   // atomic set; there is no need of synchronizing it
-    }
-
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/operations/ExistenceCheckOperation.java b/src/de/mobilcom/debitel/cloud/android/operations/ExistenceCheckOperation.java
deleted file mode 100644 (file)
index b7088b0..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/* ownCloud Android client application
- *   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 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 de.mobilcom.debitel.cloud.android.operations;
-
-import org.apache.commons.httpclient.HttpStatus;
-import org.apache.commons.httpclient.methods.HeadMethod;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-
-import eu.alefzero.webdav.WebdavClient;
-import eu.alefzero.webdav.WebdavUtils;
-import android.content.Context;
-import android.net.ConnectivityManager;
-
-/**
- * Operation to check the existence or absence of a path in a remote server.
- * 
- * @author David A. Velasco
- */
-public class ExistenceCheckOperation extends RemoteOperation {
-    
-    /** Maximum time to wait for a response from the server in MILLISECONDs.  */
-    public static final int TIMEOUT = 10000;
-    
-    private static final String TAG = ExistenceCheckOperation.class.getSimpleName();
-    
-    private String mPath;
-    private Context mContext;
-    private boolean mSuccessIfAbsent;
-
-    
-    /**
-     * Full constructor. Success of the operation will depend upon the value of successIfAbsent.
-     * 
-     * @param path              Path to append to the URL owned by the client instance.
-     * @param context           Android application context.
-     * @param successIfAbsent   When 'true', the operation finishes in success if the path does NOT exist in the remote server (HTTP 404).
-     */
-    public ExistenceCheckOperation(String path, Context context, boolean successIfAbsent) {
-        mPath = (path != null) ? path : "";
-        mContext = context;
-        mSuccessIfAbsent = successIfAbsent;
-    }
-    
-
-       @Override
-       protected RemoteOperationResult run(WebdavClient client) {
-        if (!isOnline()) {
-            return new RemoteOperationResult(RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION);
-        }
-        RemoteOperationResult result = null;
-        HeadMethod head = null;
-        try {
-            head = new HeadMethod(client.getBaseUri() + WebdavUtils.encodePath(mPath));
-            int status = client.executeMethod(head, TIMEOUT, TIMEOUT);
-            client.exhaustResponse(head.getResponseBodyAsStream());
-            boolean success = (status == HttpStatus.SC_OK && !mSuccessIfAbsent) || (status == HttpStatus.SC_NOT_FOUND && mSuccessIfAbsent);
-            result = new RemoteOperationResult(success, status, head.getResponseHeaders());
-            Log_OC.d(TAG, "Existence check for " + client.getBaseUri() + WebdavUtils.encodePath(mPath) + " targeting for " + (mSuccessIfAbsent ? " absence " : " existence ") + "finished with HTTP status " + status + (!success?"(FAIL)":""));
-            
-        } catch (Exception e) {
-            result = new RemoteOperationResult(e);
-            Log_OC.e(TAG, "Existence check for " + client.getBaseUri() + WebdavUtils.encodePath(mPath) + " targeting for " + (mSuccessIfAbsent ? " absence " : " existence ") + ": " + result.getLogMessage(), result.getException());
-            
-        } finally {
-            if (head != null)
-                head.releaseConnection();
-        }
-        return result;
-       }
-
-    private boolean isOnline() {
-        ConnectivityManager cm = (ConnectivityManager) mContext
-                .getSystemService(Context.CONNECTIVITY_SERVICE);
-        return cm != null && cm.getActiveNetworkInfo() != null
-                && cm.getActiveNetworkInfo().isConnectedOrConnecting();
-    }
-
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/operations/OAuth2GetAccessToken.java b/src/de/mobilcom/debitel/cloud/android/operations/OAuth2GetAccessToken.java
deleted file mode 100644 (file)
index 632efd8..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-package de.mobilcom.debitel.cloud.android.operations;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.commons.httpclient.methods.PostMethod;
-import org.apache.commons.httpclient.NameValuePair;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.authentication.OAuth2Constants;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-
-import eu.alefzero.webdav.WebdavClient;
-
-public class OAuth2GetAccessToken extends RemoteOperation {
-    
-    private static final String TAG = OAuth2GetAccessToken.class.getSimpleName();
-    
-    private String mClientId;
-    private String mRedirectUri;
-    private String mGrantType;
-    
-    private String mOAuth2AuthorizationResponse;
-    private Map<String, String> mOAuth2ParsedAuthorizationResponse;
-    private Map<String, String> mResultTokenMap;
-
-    
-    public OAuth2GetAccessToken(String clientId, String redirectUri, String grantType, String oAuth2AuthorizationResponse) {
-        mClientId = clientId;
-        mRedirectUri = redirectUri;
-        mGrantType = grantType;
-        mOAuth2AuthorizationResponse = oAuth2AuthorizationResponse;
-        mOAuth2ParsedAuthorizationResponse = new HashMap<String, String>();
-        mResultTokenMap = null;
-    }
-    
-    
-    public Map<String, String> getOauth2AutorizationResponse() {
-        return mOAuth2ParsedAuthorizationResponse;
-    }
-
-    public Map<String, String> getResultTokenMap() {
-        return mResultTokenMap;
-    }
-    
-    @Override
-    protected RemoteOperationResult run(WebdavClient client) {
-        RemoteOperationResult result = null;
-        PostMethod postMethod = null;
-        
-        try {
-            parseAuthorizationResponse();
-            if (mOAuth2ParsedAuthorizationResponse.keySet().contains(OAuth2Constants.KEY_ERROR)) {
-                if (OAuth2Constants.VALUE_ERROR_ACCESS_DENIED.equals(mOAuth2ParsedAuthorizationResponse.get(OAuth2Constants.KEY_ERROR))) {
-                    result = new RemoteOperationResult(ResultCode.OAUTH2_ERROR_ACCESS_DENIED);
-                } else {
-                    result = new RemoteOperationResult(ResultCode.OAUTH2_ERROR);
-                }
-            }
-            
-            if (result == null) { 
-                NameValuePair[] nameValuePairs = new NameValuePair[4];
-                nameValuePairs[0] = new NameValuePair(OAuth2Constants.KEY_GRANT_TYPE, mGrantType);
-                nameValuePairs[1] = new NameValuePair(OAuth2Constants.KEY_CODE, mOAuth2ParsedAuthorizationResponse.get(OAuth2Constants.KEY_CODE));            
-                nameValuePairs[2] = new NameValuePair(OAuth2Constants.KEY_REDIRECT_URI, mRedirectUri);       
-                nameValuePairs[3] = new NameValuePair(OAuth2Constants.KEY_CLIENT_ID, mClientId);
-                //nameValuePairs[4] = new NameValuePair(OAuth2Constants.KEY_SCOPE, mOAuth2ParsedAuthorizationResponse.get(OAuth2Constants.KEY_SCOPE));         
-                
-                postMethod = new PostMethod(client.getBaseUri().toString());
-                postMethod.setRequestBody(nameValuePairs);
-                int status = client.executeMethod(postMethod);
-                
-                String response = postMethod.getResponseBodyAsString();
-                if (response != null && response.length() > 0) {
-                    JSONObject tokenJson = new JSONObject(response);
-                    parseAccessTokenResult(tokenJson);
-                    if (mResultTokenMap.get(OAuth2Constants.KEY_ERROR) != null || mResultTokenMap.get(OAuth2Constants.KEY_ACCESS_TOKEN) == null) {
-                        result = new RemoteOperationResult(ResultCode.OAUTH2_ERROR);
-                    
-                    } else {
-                        result = new RemoteOperationResult(true, status, postMethod.getResponseHeaders());
-                    }
-                    
-                } else {
-                    client.exhaustResponse(postMethod.getResponseBodyAsStream());
-                    result = new RemoteOperationResult(false, status, postMethod.getResponseHeaders());
-                }
-            }
-            
-        } catch (Exception e) {
-            result = new RemoteOperationResult(e);
-            
-        } finally {
-            if (postMethod != null)
-                postMethod.releaseConnection();    // let the connection available for other methods
-            
-            if (result.isSuccess()) {
-                Log_OC.i(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + result.getLogMessage());
-            
-            } else if (result.getException() != null) {
-                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_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_OC.e(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + result.getLogMessage());
-            }
-        }
-        
-        return result;
-    }
-    
-    
-    private void parseAuthorizationResponse() {
-        String[] pairs = mOAuth2AuthorizationResponse.split("&");
-        int i = 0;
-        String key = "";
-        String value = "";
-        StringBuilder sb = new StringBuilder();
-        while (pairs.length > i) {
-            int j = 0;
-            String[] part = pairs[i].split("=");
-            while (part.length > j) {
-                String p = part[j];
-                if (j == 0) {
-                    key = p;
-                    sb.append(key + " = ");
-                } else if (j == 1) {
-                    value = p;
-                    mOAuth2ParsedAuthorizationResponse.put(key, value);
-                    sb.append(value + "\n");
-                }
-
-                Log_OC.v(TAG, "[" + i + "," + j + "] = " + p);
-                j++;
-            }
-            i++;
-        }
-    }
-
-
-    private void parseAccessTokenResult (JSONObject tokenJson) throws JSONException {
-        mResultTokenMap = new HashMap<String, String>();
-        
-        if (tokenJson.has(OAuth2Constants.KEY_ACCESS_TOKEN)) {
-            mResultTokenMap.put(OAuth2Constants.KEY_ACCESS_TOKEN, tokenJson.getString(OAuth2Constants.KEY_ACCESS_TOKEN));
-        }
-        if (tokenJson.has(OAuth2Constants.KEY_TOKEN_TYPE)) {
-            mResultTokenMap.put(OAuth2Constants.KEY_TOKEN_TYPE, tokenJson.getString(OAuth2Constants.KEY_TOKEN_TYPE));
-        }
-        if (tokenJson.has(OAuth2Constants.KEY_EXPIRES_IN)) {
-            mResultTokenMap.put(OAuth2Constants.KEY_EXPIRES_IN, tokenJson.getString(OAuth2Constants.KEY_EXPIRES_IN));
-        }
-        if (tokenJson.has(OAuth2Constants.KEY_REFRESH_TOKEN)) {
-            mResultTokenMap.put(OAuth2Constants.KEY_REFRESH_TOKEN, tokenJson.getString(OAuth2Constants.KEY_REFRESH_TOKEN));
-        }
-        if (tokenJson.has(OAuth2Constants.KEY_SCOPE)) {
-            mResultTokenMap.put(OAuth2Constants.KEY_SCOPE, tokenJson.getString(OAuth2Constants.KEY_SCOPE));
-        }
-        if (tokenJson.has(OAuth2Constants.KEY_ERROR)) {
-            mResultTokenMap.put(OAuth2Constants.KEY_ERROR, tokenJson.getString(OAuth2Constants.KEY_ERROR));
-        }
-        if (tokenJson.has(OAuth2Constants.KEY_ERROR_DESCRIPTION)) {
-            mResultTokenMap.put(OAuth2Constants.KEY_ERROR_DESCRIPTION, tokenJson.getString(OAuth2Constants.KEY_ERROR_DESCRIPTION));
-        }
-        if (tokenJson.has(OAuth2Constants.KEY_ERROR_URI)) {
-            mResultTokenMap.put(OAuth2Constants.KEY_ERROR_URI, tokenJson.getString(OAuth2Constants.KEY_ERROR_URI));
-        }
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/operations/OnRemoteOperationListener.java b/src/de/mobilcom/debitel/cloud/android/operations/OnRemoteOperationListener.java
deleted file mode 100644 (file)
index 3bd183e..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.operations;
-
-public interface OnRemoteOperationListener {
-
-       void onRemoteOperationFinish(RemoteOperation caller, RemoteOperationResult result);
-       
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/operations/OperationCancelledException.java b/src/de/mobilcom/debitel/cloud/android/operations/OperationCancelledException.java
deleted file mode 100644 (file)
index 8db7ee8..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.operations;
-
-public class OperationCancelledException extends Exception {
-
-    /**
-     * Generated serial version - to avoid Java warning
-     */
-    private static final long serialVersionUID = -6350981497740424983L;
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/operations/OwnCloudServerCheckOperation.java b/src/de/mobilcom/debitel/cloud/android/operations/OwnCloudServerCheckOperation.java
deleted file mode 100644 (file)
index fbf097f..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.operations;
-
-import org.apache.commons.httpclient.HttpStatus;
-import org.apache.commons.httpclient.methods.GetMethod;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-import de.mobilcom.debitel.cloud.android.utils.OwnCloudVersion;
-
-import eu.alefzero.webdav.WebdavClient;
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.Uri;
-
-public class OwnCloudServerCheckOperation extends RemoteOperation {
-    
-    /** Maximum time to wait for a response from the server when the connection is being tested, in MILLISECONDs.  */
-    public static final int TRY_CONNECTION_TIMEOUT = 5000;
-    
-    private static final String TAG = OwnCloudServerCheckOperation.class.getSimpleName();
-    
-    private String mUrl;
-    private RemoteOperationResult mLatestResult;
-    private Context mContext;
-    private OwnCloudVersion mOCVersion;
-
-    public OwnCloudServerCheckOperation(String url, Context context) {
-        mUrl = url;
-        mContext = context;
-        mOCVersion = null;
-    }
-    
-    public OwnCloudVersion getDiscoveredVersion() {
-        return mOCVersion;
-    }
-
-    private boolean tryConnection(WebdavClient wc, String urlSt) {
-        boolean retval = false;
-        GetMethod get = null;
-        try {
-            get = new GetMethod(urlSt);
-            int status = wc.executeMethod(get, TRY_CONNECTION_TIMEOUT, TRY_CONNECTION_TIMEOUT);
-            String response = get.getResponseBodyAsString();
-            if (status == HttpStatus.SC_OK) {
-                JSONObject json = new JSONObject(response);
-                if (!json.getBoolean("installed")) {
-                    mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
-                } else {
-                    mOCVersion = new OwnCloudVersion(json.getString("version"));
-                    if (!mOCVersion.isVersionValid()) {
-                        mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.BAD_OC_VERSION);
-                        
-                    } else {
-                        mLatestResult = new RemoteOperationResult(urlSt.startsWith("https://") ? 
-                                                                    RemoteOperationResult.ResultCode.OK_SSL : 
-                                                                    RemoteOperationResult.ResultCode.OK_NO_SSL
-                            );
-
-                        retval = true;
-                    }
-                }
-                
-            } else {
-                mLatestResult = new RemoteOperationResult(false, status, get.getResponseHeaders());
-            }
-
-        } catch (JSONException e) {
-            mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
-            
-        } catch (Exception e) {
-            mLatestResult = new RemoteOperationResult(e);
-            
-        } finally {
-            if (get != null)
-                get.releaseConnection();
-        }
-        
-        if (mLatestResult.isSuccess()) {
-            Log_OC.i(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage());
-            
-        } else if (mLatestResult.getException() != null) {
-            Log_OC.e(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage(), mLatestResult.getException());
-            
-        } else {
-            Log_OC.e(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage());
-        }
-
-        return retval;
-    }
-
-    private boolean isOnline() {
-        ConnectivityManager cm = (ConnectivityManager) mContext
-                .getSystemService(Context.CONNECTIVITY_SERVICE);
-        return cm != null && cm.getActiveNetworkInfo() != null
-                && cm.getActiveNetworkInfo().isConnectedOrConnecting();
-    }
-
-       @Override
-       protected RemoteOperationResult run(WebdavClient client) {
-        if (!isOnline()) {
-               return new RemoteOperationResult(RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION);
-        }
-        if (mUrl.startsWith("http://") || mUrl.startsWith("https://")) {
-            tryConnection(client, mUrl + AccountUtils.STATUS_PATH);
-            
-        } else {
-            client.setBaseUri(Uri.parse("https://" + mUrl + AccountUtils.STATUS_PATH));
-            boolean httpsSuccess = tryConnection(client, "https://" + mUrl + AccountUtils.STATUS_PATH); 
-            if (!httpsSuccess && !mLatestResult.isSslRecoverableException()) {
-                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);
-            }
-        }
-        return mLatestResult;
-       }
-       
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/operations/RemoteOperation.java b/src/de/mobilcom/debitel/cloud/android/operations/RemoteOperation.java
deleted file mode 100644 (file)
index 19bbfb2..0000000
+++ /dev/null
@@ -1,292 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.operations;
-
-import java.io.IOException;
-
-import org.apache.commons.httpclient.Credentials;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.network.BearerCredentials;
-import de.mobilcom.debitel.cloud.android.network.OwnCloudClientUtils;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.AccountsException;
-import android.app.Activity;
-import android.content.Context;
-import android.os.Handler;
-
-import eu.alefzero.webdav.WebdavClient;
-
-/**
- * Operation which execution involves one or several interactions with an ownCloud server.
- * 
- * Provides methods to execute the operation both synchronously or asynchronously.
- * 
- * @author David A. Velasco 
- */
-public abstract class RemoteOperation implements Runnable {
-       
-    private static final String TAG = RemoteOperation.class.getSimpleName();
-
-    /** ownCloud account in the remote ownCloud server to operate */
-    private Account mAccount = null;
-    
-    /** Android Application context */
-    private Context mContext = null;
-    
-       /** Object to interact with the remote server */
-       private WebdavClient mClient = null;
-       
-       /** Callback object to notify about the execution of the remote operation */
-       private OnRemoteOperationListener mListener = null;
-       
-       /** Handler to the thread where mListener methods will be called */
-       private Handler mListenerHandler = null;
-
-       /** Activity */
-    private Activity mCallerActivity;
-
-       
-       /**
-        *  Abstract method to implement the operation in derived classes.
-        */
-       protected abstract RemoteOperationResult run(WebdavClient client); 
-       
-
-    /**
-     * Synchronously executes the remote operation on the received ownCloud account.
-     * 
-     * Do not call this method from the main thread.
-     * 
-     * This method should be used whenever an ownCloud account is available, instead of {@link #execute(WebdavClient)}. 
-     * 
-     * @param account   ownCloud account in remote ownCloud server to reach during the execution of the operation.
-     * @param context   Android context for the component calling the method.
-     * @return          Result of the operation.
-     */
-    public final RemoteOperationResult execute(Account account, Context context) {
-        if (account == null)
-            throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account");
-        if (context == null)
-            throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context");
-        mAccount = account;
-        mContext = context.getApplicationContext();
-        try {
-            mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext);
-        } catch (Exception e) {
-            Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, e);
-            return new RemoteOperationResult(e);
-        }
-        return run(mClient);
-    }
-    
-       
-       /**
-        * Synchronously executes the remote operation
-        * 
-     * Do not call this method from the main thread.
-     * 
-        * @param client        Client object to reach an ownCloud server during the execution of the operation.
-        * @return                      Result of the operation.
-        */
-       public final RemoteOperationResult execute(WebdavClient client) {
-               if (client == null)
-                       throw new IllegalArgumentException("Trying to execute a remote operation with a NULL WebdavClient");
-               mClient = client;
-               return run(client);
-       }
-
-       
-    /**
-     * Asynchronously executes the remote operation
-     * 
-     * This method should be used whenever an ownCloud account is available, instead of {@link #execute(WebdavClient)}. 
-     * 
-     * @param account           ownCloud account in remote ownCloud server to reach during the execution of the operation.
-     * @param context           Android context for the component calling the method.
-     * @param listener          Listener to be notified about the execution of the operation.
-     * @param listenerHandler   Handler associated to the thread where the methods of the listener objects must be called.
-     * @return                  Thread were the remote operation is executed.
-     */
-    public final Thread execute(Account account, Context context, OnRemoteOperationListener listener, Handler listenerHandler, Activity callerActivity) {
-        if (account == null)
-            throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account");
-        if (context == null)
-            throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context");
-        mAccount = account;
-        mContext = context.getApplicationContext();
-        mCallerActivity = callerActivity;
-        mClient = null;     // the client instance will be created from mAccount and mContext in the runnerThread to create below
-        
-        if (listener == null) {
-            throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a listener to notiy the result");
-        }
-        mListener = listener;
-        
-        if (listenerHandler == null) {
-            throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a handler to the listener's thread");
-        }
-        mListenerHandler = listenerHandler;
-        
-        Thread runnerThread = new Thread(this);
-        runnerThread.start();
-        return runnerThread;
-    }
-
-    
-       /**
-        * Asynchronously executes the remote operation
-        * 
-        * @param client                        Client object to reach an ownCloud server during the execution of the operation.
-        * @param listener                      Listener to be notified about the execution of the operation.
-        * @param listenerHandler       Handler associated to the thread where the methods of the listener objects must be called.
-        * @return                                      Thread were the remote operation is executed.
-        */
-       public final Thread execute(WebdavClient client, OnRemoteOperationListener listener, Handler listenerHandler) {
-               if (client == null) {
-                       throw new IllegalArgumentException("Trying to execute a remote operation with a NULL WebdavClient");
-               }
-               mClient = client;
-               
-               if (listener == null) {
-                       throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a listener to notiy the result");
-               }
-               mListener = listener;
-               
-               if (listenerHandler == null) {
-                       throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a handler to the listener's thread");
-               }
-               mListenerHandler = listenerHandler;
-               
-               Thread runnerThread = new Thread(this);
-               runnerThread.start();
-               return runnerThread;
-       }
-       
-    /**
-     * Synchronously retries the remote operation using the same WebdavClient in the last call to {@link RemoteOperation#execute(WebdavClient)}
-     * 
-     * @param listener          Listener to be notified about the execution of the operation.
-     * @param listenerHandler   Handler associated to the thread where the methods of the listener objects must be called.
-     * @return                  Thread were the remote operation is executed.
-     */
-    public final RemoteOperationResult retry() {
-        return execute(mClient);
-    }
-    
-    /**
-     * Asynchronously retries the remote operation using the same WebdavClient in the last call to {@link RemoteOperation#execute(WebdavClient, OnRemoteOperationListener, Handler)}
-     * 
-     * @param listener          Listener to be notified about the execution of the operation.
-     * @param listenerHandler   Handler associated to the thread where the methods of the listener objects must be called.
-     * @return                  Thread were the remote operation is executed.
-     */
-    public final Thread retry(OnRemoteOperationListener listener, Handler listenerHandler) {
-        return execute(mClient, listener, listenerHandler);
-    }
-       
-       
-       /**
-        * Asynchronous execution of the operation 
-        * started by {@link RemoteOperation#execute(WebdavClient, OnRemoteOperationListener, Handler)}, 
-        * and result posting.
-        * 
-        * TODO refactor && clean the code; now it's a mess
-        */
-    @Override
-    public final void run() {
-        RemoteOperationResult result = null;
-        boolean repeat = false;
-        do {
-            try{
-                if (mClient == null) {
-                    if (mAccount != null && mContext != null) {
-                        if (mCallerActivity != null) {
-                            mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext, mCallerActivity);
-                        } else {
-                            mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext);
-                        }
-                    } else {
-                        throw new IllegalStateException("Trying to run a remote operation asynchronously with no client instance or account");
-                    }
-                }
-            
-            } catch (IOException 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_OC.e(TAG, "Error while trying to access to " + mAccount.name, e);
-                result = new RemoteOperationResult(e);
-            }
-       
-            if (result == null)
-                result = run(mClient);
-        
-            repeat = false;
-            if (mCallerActivity != null && mAccount != null && mContext != null && !result.isSuccess() &&
-//                    (result.getCode() == ResultCode.UNAUTHORIZED || (result.isTemporalRedirection() && result.isIdPRedirection()))) {
-                    (result.getCode() == ResultCode.UNAUTHORIZED || result.isIdPRedirection())) {
-                /// possible fail due to lack of authorization in an operation performed in foreground
-                Credentials cred = mClient.getCredentials();
-                String ssoSessionCookie = mClient.getSsoSessionCookie();
-                if (cred != null || ssoSessionCookie != null) {
-                    /// confirmed : unauthorized operation
-                    AccountManager am = AccountManager.get(mContext);
-                    boolean bearerAuthorization = (cred != null && cred instanceof BearerCredentials);
-                    boolean samlBasedSsoAuthorization = (cred == null && ssoSessionCookie != null);
-                    if (bearerAuthorization) {
-                        am.invalidateAuthToken(MainApp.getAccountType(), ((BearerCredentials)cred).getAccessToken());
-                    } else if (samlBasedSsoAuthorization ) {
-                        am.invalidateAuthToken(MainApp.getAccountType(), ssoSessionCookie);
-                    } else {
-                        am.clearPassword(mAccount);
-                    }
-                    mClient = null;
-                    repeat = true;  // when repeated, the creation of a new OwnCloudClient after erasing the saved credentials will trigger the login activity
-                    result = null;
-                }
-            }
-        } while (repeat);
-        
-        final RemoteOperationResult resultToSend = result;
-        if (mListenerHandler != null && mListener != null) {
-               mListenerHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend);
-                }
-            });
-        }
-    }
-
-
-    /**
-     * Returns the current client instance to access the remote server.
-     * 
-     * @return      Current client instance to access the remote server.
-     */
-    public final WebdavClient getClient() {
-        return mClient;
-    }
-
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/operations/RemoteOperationResult.java b/src/de/mobilcom/debitel/cloud/android/operations/RemoteOperationResult.java
deleted file mode 100644 (file)
index c5b7dba..0000000
+++ /dev/null
@@ -1,337 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.operations;
-
-import java.io.IOException;
-import java.io.Serializable;
-import java.net.MalformedURLException;
-import java.net.SocketException;
-import java.net.SocketTimeoutException;
-import java.net.UnknownHostException;
-
-import javax.net.ssl.SSLException;
-
-import org.apache.commons.httpclient.ConnectTimeoutException;
-import org.apache.commons.httpclient.Header;
-import org.apache.commons.httpclient.HttpException;
-import org.apache.commons.httpclient.HttpStatus;
-import org.apache.jackrabbit.webdav.DavException;
-
-import android.accounts.Account;
-import android.accounts.AccountsException;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils.AccountNotFoundException;
-import de.mobilcom.debitel.cloud.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.
- * 
- * @author David A. Velasco
- */
-public class RemoteOperationResult implements Serializable {
-
-    /** Generated - should be refreshed every time the class changes!! */
-    private static final long serialVersionUID = -4415103901492836870L;
-    
-
-    
-    private static final String TAG = "RemoteOperationResult";
-    
-    public enum ResultCode { 
-        OK,
-        OK_SSL,
-        OK_NO_SSL,
-        UNHANDLED_HTTP_CODE,
-        UNAUTHORIZED,        
-        FILE_NOT_FOUND, 
-        INSTANCE_NOT_CONFIGURED, 
-        UNKNOWN_ERROR, 
-        WRONG_CONNECTION,  
-        TIMEOUT, 
-        INCORRECT_ADDRESS, 
-        HOST_NOT_AVAILABLE, 
-        NO_NETWORK_CONNECTION, 
-        SSL_ERROR,
-        SSL_RECOVERABLE_PEER_UNVERIFIED,
-        BAD_OC_VERSION,
-        CANCELLED, 
-        INVALID_LOCAL_FILE_NAME, 
-        INVALID_OVERWRITE,
-        CONFLICT, 
-        OAUTH2_ERROR,
-        SYNC_CONFLICT,
-        LOCAL_STORAGE_FULL, 
-        LOCAL_STORAGE_NOT_MOVED, 
-        LOCAL_STORAGE_NOT_COPIED, 
-        OAUTH2_ERROR_ACCESS_DENIED,
-        QUOTA_EXCEEDED, 
-        ACCOUNT_NOT_FOUND, 
-        ACCOUNT_EXCEPTION, 
-        ACCOUNT_NOT_NEW, 
-        ACCOUNT_NOT_THE_SAME
-    }
-
-    private boolean mSuccess = false;
-    private int mHttpCode = -1;
-    private Exception mException = null;
-    private ResultCode mCode = ResultCode.UNKNOWN_ERROR;
-    private String mRedirectedLocation;
-
-    public RemoteOperationResult(ResultCode code) {
-        mCode = code;
-        mSuccess = (code == ResultCode.OK || code == ResultCode.OK_SSL || code == ResultCode.OK_NO_SSL);
-    }
-
-    private RemoteOperationResult(boolean success, int httpCode) {
-        mSuccess = success;
-        mHttpCode = httpCode;
-
-        if (success) {
-            mCode = ResultCode.OK;
-
-        } else if (httpCode > 0) {
-            switch (httpCode) {
-            case HttpStatus.SC_UNAUTHORIZED:
-                mCode = ResultCode.UNAUTHORIZED;
-                break;
-            case HttpStatus.SC_NOT_FOUND:
-                mCode = ResultCode.FILE_NOT_FOUND;
-                break;
-            case HttpStatus.SC_INTERNAL_SERVER_ERROR:
-                mCode = ResultCode.INSTANCE_NOT_CONFIGURED;
-                break;
-            case HttpStatus.SC_CONFLICT:
-                mCode = ResultCode.CONFLICT;
-                break;
-            case HttpStatus.SC_INSUFFICIENT_STORAGE:
-                mCode = ResultCode.QUOTA_EXCEEDED;
-                break;
-            default:
-                mCode = ResultCode.UNHANDLED_HTTP_CODE;
-                Log_OC.d(TAG, "RemoteOperationResult has processed UNHANDLED_HTTP_CODE: " + httpCode);
-            }
-        }
-    }
-    
-    public RemoteOperationResult(boolean success, int httpCode, Header[] headers) {
-        this(success, httpCode);
-        if (headers != null) {
-            Header current;
-            for (int i=0; i<headers.length; i++) {
-                current = headers[i];
-                if ("Location".equals(current.getName())) {
-                    mRedirectedLocation = current.getValue();
-                    break;
-                }
-            }
-        }
-    }    
-
-    public RemoteOperationResult(Exception e) {
-        mException = e;
-
-        if (e instanceof OperationCancelledException) {
-            mCode = ResultCode.CANCELLED;
-
-        } 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 AccountNotFoundException) {
-            mCode = ResultCode.ACCOUNT_NOT_FOUND;
-            
-        } else if (e instanceof AccountsException) {
-            mCode = ResultCode.ACCOUNT_EXCEPTION;
-            
-        } else if (e instanceof SSLException || e instanceof RuntimeException) {
-            CertificateCombinedException se = getCertificateCombinedException(e);
-            if (se != null) {
-                mException = se;
-                if (se.isRecoverable()) {
-                    mCode = ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED;
-                }
-            } 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;
-    }
-
-    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;
-        }
-        Throwable cause = mException.getCause();
-        Throwable previousCause = null;
-        while (cause != null && cause != previousCause && !(cause instanceof CertificateCombinedException)) {
-            previousCause = cause;
-            cause = cause.getCause();
-        }
-        if (cause != null && cause instanceof CertificateCombinedException) {
-            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) {
-                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";
-
-            } else if (mException instanceof DavException) {
-                return "Unexpected WebDAV exception";
-
-            } else if (mException instanceof HttpException) {
-                return "HTTP violation";
-
-            } else if (mException instanceof IOException) {
-                return "Unrecovered transport exception";
-
-            } else if (mException instanceof AccountNotFoundException) {
-                Account failedAccount = ((AccountNotFoundException)mException).getFailedAccount();
-                return mException.getMessage() + " (" + (failedAccount != null ? failedAccount.name : "NULL") + ")";
-                
-            } else if (mException instanceof AccountsException) {
-                return "Exception while using account";
-                
-            } else {
-                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";
-
-        } else if (mCode == ResultCode.ACCOUNT_NOT_NEW) {
-            return "Account already existing when creating a new one";
-
-        } else if (mCode == ResultCode.ACCOUNT_NOT_THE_SAME) {
-            return "Authenticated with a different account than the one updating";
-        }
-
-        return "Operation finished with HTTP status code " + mHttpCode + " (" + (isSuccess() ? "success" : "fail") + ")";
-
-    }
-
-    public boolean isServerFail() {
-        return (mHttpCode >= HttpStatus.SC_INTERNAL_SERVER_ERROR);
-    }
-
-    public boolean isException() {
-        return (mException != null);
-    }
-
-    public boolean isTemporalRedirection() {
-        return (mHttpCode == 302 || mHttpCode == 307);
-    }
-
-    public String getRedirectedLocation() {
-        return mRedirectedLocation;
-    }
-    
-    public boolean isIdPRedirection() {
-        return (mRedirectedLocation != null &&
-                (mRedirectedLocation.toUpperCase().contains("SAML") || 
-                mRedirectedLocation.toLowerCase().contains("wayf")));
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/operations/RemoveFileOperation.java b/src/de/mobilcom/debitel/cloud/android/operations/RemoveFileOperation.java
deleted file mode 100644 (file)
index 03c4cb8..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.operations;
-
-import org.apache.commons.httpclient.HttpStatus;
-import org.apache.jackrabbit.webdav.client.methods.DeleteMethod;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-
-import eu.alefzero.webdav.WebdavClient;
-import eu.alefzero.webdav.WebdavUtils;
-
-/**
- * Remote operation performing the removal of a remote file or folder in the ownCloud server.
- * 
- * @author David A. Velasco
- */
-public class RemoveFileOperation extends RemoteOperation {
-    
-    private static final String TAG = RemoveFileOperation.class.getSimpleName();
-
-    private static final int REMOVE_READ_TIMEOUT = 10000;
-    private static final int REMOVE_CONNECTION_TIMEOUT = 5000;
-    
-    OCFile mFileToRemove;
-    boolean mDeleteLocalCopy;
-    DataStorageManager mDataStorageManager;
-    
-    
-    /**
-     * Constructor
-     * 
-     * @param fileToRemove          OCFile instance describing the remote file or folder to remove from the server
-     * @param deleteLocalCopy       When 'true', and a local copy of the file exists, it is also removed.
-     * @param storageManager        Reference to the local database corresponding to the account where the file is contained. 
-     */
-    public RemoveFileOperation(OCFile fileToRemove, boolean deleteLocalCopy, DataStorageManager storageManager) {
-        mFileToRemove = fileToRemove;
-        mDeleteLocalCopy = deleteLocalCopy;
-        mDataStorageManager = storageManager;
-    }
-    
-    
-    /**
-     * Getter for the file to remove (or removed, if the operation was successfully performed).
-     * 
-     * @return      File to remove or already removed.
-     */
-    public OCFile getFile() {
-        return mFileToRemove;
-    }
-    
-    
-    /**
-     * Performs the remove operation
-     * 
-     * @param   client      Client object to communicate with the remote ownCloud server.
-     */
-    @Override
-    protected RemoteOperationResult run(WebdavClient client) {
-        RemoteOperationResult result = null;
-        DeleteMethod delete = null;
-        try {
-            delete = new DeleteMethod(client.getBaseUri() + WebdavUtils.encodePath(mFileToRemove.getRemotePath()));
-            int status = client.executeMethod(delete, REMOVE_READ_TIMEOUT, REMOVE_CONNECTION_TIMEOUT);
-            if (delete.succeeded() || status == HttpStatus.SC_NOT_FOUND) {
-                if (mFileToRemove.isDirectory()) {
-                    mDataStorageManager.removeDirectory(mFileToRemove, true, mDeleteLocalCopy);
-                } else {
-                    mDataStorageManager.removeFile(mFileToRemove, mDeleteLocalCopy);
-                }
-            }
-            delete.getResponseBodyAsString();   // exhaust the response, although not interesting
-            result = new RemoteOperationResult((delete.succeeded() || status == HttpStatus.SC_NOT_FOUND), status, delete.getResponseHeaders());
-            Log_OC.i(TAG, "Remove " + mFileToRemove.getRemotePath() + ": " + result.getLogMessage());
-            
-        } catch (Exception e) {
-            result = new RemoteOperationResult(e);
-            Log_OC.e(TAG, "Remove " + mFileToRemove.getRemotePath() + ": " + result.getLogMessage(), e);
-            
-        } finally {
-            if (delete != null)
-                delete.releaseConnection();
-        }
-        return result;
-    }
-    
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/operations/RenameFileOperation.java b/src/de/mobilcom/debitel/cloud/android/operations/RenameFileOperation.java
deleted file mode 100644 (file)
index 60e1b03..0000000
+++ /dev/null
@@ -1,247 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.operations;
-
-import java.io.File;
-import java.io.IOException;
-
-import org.apache.jackrabbit.webdav.client.methods.DavMethodBase;
-//import org.apache.jackrabbit.webdav.client.methods.MoveMethod;
-
-import android.accounts.Account;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-import de.mobilcom.debitel.cloud.android.utils.FileStorageUtils;
-
-import eu.alefzero.webdav.WebdavClient;
-import eu.alefzero.webdav.WebdavUtils;
-
-/**
- * Remote operation performing the rename of a remote file (or folder?) in the ownCloud server.
- * 
- * @author David A. Velasco
- */
-public class RenameFileOperation extends RemoteOperation {
-    
-    private static final String TAG = RenameFileOperation.class.getSimpleName();
-
-    private static final int RENAME_READ_TIMEOUT = 10000;
-    private static final int RENAME_CONNECTION_TIMEOUT = 5000;
-    
-
-    private OCFile mFile;
-    private Account mAccount;
-    private String mNewName;
-    private String mNewRemotePath;
-    private DataStorageManager mStorageManager;
-    
-    
-    /**
-     * Constructor
-     * 
-     * @param file                  OCFile instance describing the remote file or folder to rename
-     * @param account               OwnCloud account containing the remote file 
-     * @param newName               New name to set as the name of file.
-     * @param storageManager        Reference to the local database corresponding to the account where the file is contained. 
-     */
-    public RenameFileOperation(OCFile file, Account account, String newName, DataStorageManager storageManager) {
-        mFile = file;
-        mAccount = account;
-        mNewName = newName;
-        mNewRemotePath = null;
-        mStorageManager = storageManager;
-    }
-  
-    public OCFile getFile() {
-        return mFile;
-    }
-    
-    
-    /**
-     * Performs the rename operation.
-     * 
-     * @param   client      Client object to communicate with the remote ownCloud server.
-     */
-    @Override
-    protected RemoteOperationResult run(WebdavClient client) {
-        RemoteOperationResult result = null;
-        
-        LocalMoveMethod move = null;
-        mNewRemotePath = null;
-        try {
-            if (mNewName.equals(mFile.getFileName())) {
-                return new RemoteOperationResult(ResultCode.OK);
-            }
-        
-            String parent = (new File(mFile.getRemotePath())).getParent();
-            parent = (parent.endsWith(OCFile.PATH_SEPARATOR)) ? parent : parent + OCFile.PATH_SEPARATOR; 
-            mNewRemotePath =  parent + mNewName;
-            if (mFile.isDirectory()) {
-                mNewRemotePath += OCFile.PATH_SEPARATOR;
-            }
-            
-            // check if the new name is valid in the local file system
-            if (!isValidNewName()) {
-                return new RemoteOperationResult(ResultCode.INVALID_LOCAL_FILE_NAME);
-            }
-        
-            // check if a file with the new name already exists
-            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);
-            }
-            move = new LocalMoveMethod( client.getBaseUri() + WebdavUtils.encodePath(mFile.getRemotePath()),
-                                        client.getBaseUri() + WebdavUtils.encodePath(mNewRemotePath));
-            int status = client.executeMethod(move, RENAME_READ_TIMEOUT, RENAME_CONNECTION_TIMEOUT);
-            if (move.succeeded()) {
-
-                if (mFile.isDirectory()) {
-                    saveLocalDirectory();
-                    
-                } else {
-                    saveLocalFile();
-                    
-                }
-             
-            /* 
-             *} else if (mFile.isDirectory() && (status == 207 || status >= 500)) {
-             *   // TODO 
-             *   // if server fails in the rename of a folder, some children files could have been moved to a folder with the new name while some others
-             *   // stayed in the old folder;
-             *   //
-             *   // easiest and heaviest solution is synchronizing the parent folder (or the full account);
-             *   //
-             *   // a better solution is synchronizing the folders with the old and new names;
-             *}
-             */
-                
-            }
-            
-            move.getResponseBodyAsString(); // exhaust response, although not interesting
-            result = new RemoteOperationResult(move.succeeded(), status, move.getResponseHeaders());
-            Log_OC.i(TAG, "Rename " + mFile.getRemotePath() + " to " + mNewRemotePath + ": " + result.getLogMessage());
-            
-        } catch (Exception e) {
-            result = new RemoteOperationResult(e);
-            Log_OC.e(TAG, "Rename " + mFile.getRemotePath() + " to " + ((mNewRemotePath==null) ? mNewName : mNewRemotePath) + ": " + result.getLogMessage(), e);
-            
-        } finally {
-            if (move != null)
-                move.releaseConnection();
-        }
-        return result;
-    }
-
-    
-    private void saveLocalDirectory() {
-        mStorageManager.moveDirectory(mFile, mNewRemotePath);
-        String localPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile);
-        File localDir = new File(localPath);
-        if (localDir.exists()) {
-            localDir.renameTo(new File(FileStorageUtils.getSavePath(mAccount.name) + mNewRemotePath));
-            // TODO - if renameTo fails, children files that are already down will result unlinked
-        }
-    }
-
-    private void saveLocalFile() {
-        mFile.setFileName(mNewName);
-        
-        // try to rename the local copy of the file
-        if (mFile.isDown()) {
-            File f = new File(mFile.getStoragePath());
-            String parentStoragePath = f.getParent();
-            if (!parentStoragePath.endsWith(File.separator))
-                parentStoragePath += File.separator;
-            if (f.renameTo(new File(parentStoragePath + mNewName))) {
-                mFile.setStoragePath(parentStoragePath + mNewName);
-            }
-            // else - NOTHING: the link to the local file is kept although the local name can't be updated
-            // TODO - study conditions when this could be a problem
-        }
-        
-        mStorageManager.saveFile(mFile);
-    }
-
-    /**
-     * Checks if the new name to set is valid in the file system 
-     * 
-     * The only way to be sure is trying to create a file with that name. It's made in the temporal directory
-     * for downloads, out of any account, and then removed. 
-     * 
-     * IMPORTANT: The test must be made in the same file system where files are download. The internal storage
-     * could be formatted with a different file system.
-     * 
-     * 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.
-     * @throws IOException  When the temporal folder can not be created.
-     */
-    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 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_OC.i(TAG, "Test for validity of name " + mNewName + " in the file system failed");
-            return false;
-        }
-        boolean result = (testFile.exists() && testFile.isFile());
-        
-        // cleaning ; result is ignored, since there is not much we could do in case of failure, but repeat and repeat...
-        testFile.delete();
-        
-        return result;
-    }
-
-
-    // move operation
-    private class LocalMoveMethod extends DavMethodBase {
-
-        public LocalMoveMethod(String uri, String dest) {
-            super(uri);
-            addRequestHeader(new org.apache.commons.httpclient.Header("Destination", dest));
-        }
-
-        @Override
-        public String getName() {
-            return "MOVE";
-        }
-
-        @Override
-        protected boolean isSuccess(int status) {
-            return status == 201 || status == 204;
-        }
-            
-    }
-    
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/operations/SynchronizeFileOperation.java b/src/de/mobilcom/debitel/cloud/android/operations/SynchronizeFileOperation.java
deleted file mode 100644 (file)
index bd5637e..0000000
+++ /dev/null
@@ -1,234 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.operations;
-
-import org.apache.http.HttpStatus;
-import org.apache.jackrabbit.webdav.DavConstants;
-import org.apache.jackrabbit.webdav.MultiStatus;
-import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
-
-import android.accounts.Account;
-import android.content.Context;
-import android.content.Intent;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.files.services.FileDownloader;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-
-import eu.alefzero.webdav.WebdavClient;
-import eu.alefzero.webdav.WebdavEntry;
-import eu.alefzero.webdav.WebdavUtils;
-
-public class SynchronizeFileOperation extends RemoteOperation {
-
-    private String TAG = SynchronizeFileOperation.class.getSimpleName();
-    private static final int SYNC_READ_TIMEOUT = 10000;
-    private static final int SYNC_CONNECTION_TIMEOUT = 5000;
-    
-    private OCFile mLocalFile;
-    private OCFile mServerFile;
-    private DataStorageManager mStorageManager;
-    private Account mAccount;
-    private boolean mSyncFileContents;
-    private boolean mLocalChangeAlreadyKnown;
-    private Context mContext;
-    
-    private boolean mTransferWasRequested = false;
-    
-    public SynchronizeFileOperation(
-            OCFile localFile,
-            OCFile serverFile,          // make this null to let the operation checks the server; added to reuse info from SynchronizeFolderOperation 
-            DataStorageManager storageManager, 
-            Account account, 
-            boolean syncFileContents,
-            boolean localChangeAlreadyKnown, 
-            Context context) {
-        
-        mLocalFile = localFile;
-        mServerFile = serverFile;
-        mStorageManager = storageManager;
-        mAccount = account;
-        mSyncFileContents = syncFileContents;
-        mLocalChangeAlreadyKnown = localChangeAlreadyKnown;
-        mContext = context;
-    }
-
-
-    @Override
-    protected RemoteOperationResult run(WebdavClient client) {
-        
-        PropFindMethod propfind = null;
-        RemoteOperationResult result = null;
-        mTransferWasRequested = false;
-        try {
-            if (!mLocalFile.isDown()) {
-                /// easy decision
-                requestForDownload(mLocalFile);
-                result = new RemoteOperationResult(ResultCode.OK);
-                
-            } else {
-                /// local copy in the device -> need to think a bit more before do anything
-                
-                if (mServerFile == null) {
-                    /// take the duty of check the server for the current state of the file there
-                    propfind = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mLocalFile.getRemotePath()),
-                            DavConstants.PROPFIND_ALL_PROP,
-                            DavConstants.DEPTH_0);
-                    int status = client.executeMethod(propfind, SYNC_READ_TIMEOUT, SYNC_CONNECTION_TIMEOUT);
-                    boolean isMultiStatus = status == HttpStatus.SC_MULTI_STATUS;
-                    if (isMultiStatus) {
-                        MultiStatus resp = propfind.getResponseBodyAsMultiStatus();
-                        WebdavEntry we = new WebdavEntry(resp.getResponses()[0],
-                                               client.getBaseUri().getPath());
-                        mServerFile = fillOCFile(we);
-                        mServerFile.setLastSyncDateForProperties(System.currentTimeMillis());
-                        
-                    } else {
-                        client.exhaustResponse(propfind.getResponseBodyAsStream());
-                        result = new RemoteOperationResult(false, status, propfind.getResponseHeaders());
-                    }
-                }
-                
-                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;
-                    if (mServerFile.getEtag() != null) {
-                        serverChanged = (!mServerFile.getEtag().equals(mLocalFile.getEtag()));   // TODO could this be dangerous when the user upgrades the server from non-tagged to tagged?
-                    } else {
-                        // server without etags
-                        serverChanged = (mServerFile.getModificationTimestamp() > mLocalFile.getModificationTimestampAtLastSyncForData());
-                    }
-                    boolean localChanged = (mLocalChangeAlreadyKnown || mLocalFile.getLocalModificationTimestamp() > mLocalFile.getLastSyncDateForData());
-                        // TODO this will be always true after the app is upgraded to database version 2; will result in unnecessary uploads
-              
-                    /// decide action to perform depending upon changes
-                    if (localChanged && serverChanged) {
-                        result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
-                  
-                    } else if (localChanged) {
-                        if (mSyncFileContents) {
-                            requestForUpload(mLocalFile);
-                            // the local update of file properties will be done by the FileUploader service when the upload finishes
-                        } else {
-                            // NOTHING TO DO HERE: updating the properties of the file in the server without uploading the contents would be stupid; 
-                            // So, an instance of SynchronizeFileOperation created with syncFileContents == false is completely useless when we suspect
-                            // that an upload is necessary (for instance, in FileObserverService).
-                        }
-                        result = new RemoteOperationResult(ResultCode.OK);
-                  
-                    } else if (serverChanged) {
-                        if (mSyncFileContents) {
-                            requestForDownload(mLocalFile); // local, not server; we won't to keep the value of keepInSync!
-                            // the update of local data will be done later by the FileUploader service when the upload finishes
-                        } else {
-                            // TODO CHECK: is this really useful in some point in the code?
-                            mServerFile.setKeepInSync(mLocalFile.keepInSync());
-                            mServerFile.setLastSyncDateForData(mLocalFile.getLastSyncDateForData());
-                            mServerFile.setStoragePath(mLocalFile.getStoragePath());
-                            mServerFile.setParentId(mLocalFile.getParentId());
-                            mStorageManager.saveFile(mServerFile);
-                            
-                        }
-                        result = new RemoteOperationResult(ResultCode.OK);
-              
-                    } else {
-                        // nothing changed, nothing to do
-                        result = new RemoteOperationResult(ResultCode.OK);
-                    }
-              
-                } 
-          
-            }
-            
-            Log_OC.i(TAG, "Synchronizing " + mAccount.name + ", file " + mLocalFile.getRemotePath() + ": " + result.getLogMessage());
-          
-        } catch (Exception e) {
-            result = new RemoteOperationResult(e);
-            Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", file " + (mLocalFile != null ? mLocalFile.getRemotePath() : "NULL") + ": " + result.getLogMessage(), result.getException());
-
-        } finally {
-            if (propfind != null)
-                propfind.releaseConnection();
-        }
-        return result;
-    }
-
-    
-    /**
-     * Requests for an upload to the FileUploader service
-     * 
-     * @param file     OCFile object representing the file to upload
-     */
-    private void requestForUpload(OCFile file) {
-        Intent i = new Intent(mContext, FileUploader.class);
-        i.putExtra(FileUploader.KEY_ACCOUNT, mAccount);
-        i.putExtra(FileUploader.KEY_FILE, file);
-        /*i.putExtra(FileUploader.KEY_REMOTE_FILE, mRemotePath);    // doing this we would lose the value of keepInSync in the road, and maybe it's not updated in the database when the FileUploader service gets it!  
-        i.putExtra(FileUploader.KEY_LOCAL_FILE, localFile.getStoragePath());*/
-        i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
-        i.putExtra(FileUploader.KEY_FORCE_OVERWRITE, true);
-        mContext.startService(i);
-        mTransferWasRequested = true;
-    }
-
-
-    /**
-     * Requests for a download to the FileDownloader service
-     * 
-     * @param file     OCFile object representing the file to download
-     */
-    private void requestForDownload(OCFile file) {
-        Intent i = new Intent(mContext, FileDownloader.class);
-        i.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount);
-        i.putExtra(FileDownloader.EXTRA_FILE, file);
-        mContext.startService(i);
-        mTransferWasRequested = true;
-    }
-
-
-    /**
-     * Creates and populates a new {@link OCFile} object with the data read from the server.
-     * 
-     * @param we        WebDAV entry read from the server for a WebDAV resource (remote file or folder).
-     * @return          New OCFile instance representing the remote resource described by we.
-     */
-    private OCFile fillOCFile(WebdavEntry we) {
-        OCFile file = new OCFile(we.decodedPath());
-        file.setCreationTimestamp(we.createTimestamp());
-        file.setFileLength(we.contentLength());
-        file.setMimetype(we.contentType());
-        file.setModificationTimestamp(we.modifiedTimestamp());
-        return file;
-    }
-
-
-    public boolean transferWasRequested() {
-        return mTransferWasRequested;
-    }
-
-
-    public OCFile getLocalFile() {
-        return mLocalFile;
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/operations/SynchronizeFolderOperation.java b/src/de/mobilcom/debitel/cloud/android/operations/SynchronizeFolderOperation.java
deleted file mode 100644 (file)
index d089f38..0000000
+++ /dev/null
@@ -1,364 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.operations;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Vector;
-
-import org.apache.http.HttpStatus;
-import org.apache.jackrabbit.webdav.DavConstants;
-import org.apache.jackrabbit.webdav.MultiStatus;
-import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
-
-import android.accounts.Account;
-import android.content.Context;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-import de.mobilcom.debitel.cloud.android.utils.FileStorageUtils;
-
-import eu.alefzero.webdav.WebdavClient;
-import eu.alefzero.webdav.WebdavEntry;
-import eu.alefzero.webdav.WebdavUtils;
-
-
-/**
- * Remote operation performing the synchronization a the contents of a remote folder with the local database
- * 
- * @author David A. Velasco
- */
-public class SynchronizeFolderOperation extends RemoteOperation {
-
-    private static final String TAG = SynchronizeFolderOperation.class.getSimpleName();
-
-    /** Remote folder to synchronize */
-    private String mRemotePath;
-    
-    /** Timestamp for the synchronization in progress */
-    private long mCurrentSyncTime;
-    
-    /** Id of the folder to synchronize in the local database */
-    private long mParentId;
-    
-    /** Access to the local database */
-    private DataStorageManager mStorageManager;
-    
-    /** Account where the file to synchronize belongs */
-    private Account mAccount;
-    
-    /** Android context; necessary to send requests to the download service; maybe something to refactor */
-    private Context mContext;
-    
-    /** Files and folders contained in the synchronized folder */
-    private List<OCFile> mChildren;
-
-    private int mConflictsFound;
-
-    private int mFailsInFavouritesFound;
-
-    private Map<String, String> mForgottenLocalFiles;
-    
-    
-    public SynchronizeFolderOperation(  String remotePath, 
-                                        long currentSyncTime, 
-                                        long parentId, 
-                                        DataStorageManager dataStorageManager, 
-                                        Account account, 
-                                        Context context ) {
-        mRemotePath = remotePath;
-        mCurrentSyncTime = currentSyncTime;
-        mParentId = parentId;
-        mStorageManager = dataStorageManager;
-        mAccount = account;
-        mContext = context;
-        mForgottenLocalFiles = new HashMap<String, String>();
-    }
-    
-    
-    public int getConflictsFound() {
-        return mConflictsFound;
-    }
-    
-    public int getFailsInFavouritesFound() {
-        return mFailsInFavouritesFound;
-    }
-    
-    public Map<String, String> getForgottenLocalFiles() {
-        return mForgottenLocalFiles;
-    }
-    
-    /**
-     * Returns the list of files and folders contained in the synchronized folder, if called after synchronization is complete.
-     * 
-     * @return      List of files and folders contained in the synchronized folder.
-     */
-    public List<OCFile> getChildren() {
-        return mChildren;
-    }
-    
-    
-    @Override
-    protected RemoteOperationResult run(WebdavClient client) {
-        RemoteOperationResult result = null;
-        mFailsInFavouritesFound = 0;
-        mConflictsFound = 0;
-        mForgottenLocalFiles.clear();
-        
-        // code before in FileSyncAdapter.fetchData
-        PropFindMethod query = null;
-        try {
-            Log_OC.d(TAG, "Synchronizing " + mAccount.name + ", fetching files in " + mRemotePath);
-            
-            // remote request 
-            query = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath),
-                    DavConstants.PROPFIND_ALL_PROP,
-                    DavConstants.DEPTH_1);
-            int status = client.executeMethod(query);
-            
-            // check and process response   - /// TODO take into account all the possible status per child-resource
-            if (isMultiStatus(status)) { 
-                MultiStatus resp = query.getResponseBodyAsMultiStatus();
-            
-                // synchronize properties of the parent folder, if necessary
-                if (mParentId == DataStorageManager.ROOT_PARENT_ID) {
-                    WebdavEntry we = new WebdavEntry(resp.getResponses()[0], client.getBaseUri().getPath());
-                    OCFile parent = fillOCFile(we);
-                    mStorageManager.saveFile(parent);
-                    mParentId = parent.getFileId();
-                }
-                
-                // read contents in folder
-                List<OCFile> updatedFiles = new Vector<OCFile>(resp.getResponses().length - 1);
-                List<SynchronizeFileOperation> filesToSyncContents = new Vector<SynchronizeFileOperation>();
-                for (int i = 1; i < resp.getResponses().length; ++i) {
-                    /// new OCFile instance with the data from the server
-                    WebdavEntry we = new WebdavEntry(resp.getResponses()[i], client.getBaseUri().getPath());
-                    OCFile file = fillOCFile(we);
-                    
-                    /// set data about local state, keeping unchanged former data if existing
-                    file.setLastSyncDateForProperties(mCurrentSyncTime);
-                    OCFile oldFile = mStorageManager.getFileByPath(file.getRemotePath());
-                    if (oldFile != null) {
-                        file.setKeepInSync(oldFile.keepInSync());
-                        file.setLastSyncDateForData(oldFile.getLastSyncDateForData());
-                        file.setModificationTimestampAtLastSyncForData(oldFile.getModificationTimestampAtLastSyncForData());    // must be kept unchanged when the file contents are not updated
-                        checkAndFixForeignStoragePath(oldFile);
-                        file.setStoragePath(oldFile.getStoragePath());
-                    }
-
-                    /// scan default location if local copy of file is not linked in OCFile instance
-                    if (file.getStoragePath() == null && !file.isDirectory()) {
-                        File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
-                        if (f.exists()) {
-                            file.setStoragePath(f.getAbsolutePath());
-                            file.setLastSyncDateForData(f.lastModified());
-                        }
-                    }
-                    
-                    /// prepare content synchronization for kept-in-sync files
-                    if (file.keepInSync()) {
-                        SynchronizeFileOperation operation = new SynchronizeFileOperation(  oldFile,        
-                                                                                            file, 
-                                                                                            mStorageManager,
-                                                                                            mAccount,       
-                                                                                            true, 
-                                                                                            false,          
-                                                                                            mContext
-                                                                                            );
-                        filesToSyncContents.add(operation);
-                    }
-                
-                    updatedFiles.add(file);
-                }
-                                
-                // save updated contents in local database; all at once, trying to get a best performance in database update (not a big deal, indeed)
-                mStorageManager.saveFiles(updatedFiles);
-                
-                // request for the synchronization of files AFTER saving last properties
-                SynchronizeFileOperation op = null;
-                RemoteOperationResult contentsResult = null;
-                for (int i=0; i < filesToSyncContents.size(); i++) {
-                    op = filesToSyncContents.get(i);
-                    contentsResult = op.execute(client);   // returns without waiting for upload or download finishes
-                    if (!contentsResult.isSuccess()) {
-                        if (contentsResult.getCode() == ResultCode.SYNC_CONFLICT) {
-                            mConflictsFound++;
-                        } else {
-                            mFailsInFavouritesFound++;
-                            if (contentsResult.getException() != null) {
-                                Log_OC.e(TAG, "Error while synchronizing favourites : " +  contentsResult.getLogMessage(), contentsResult.getException());
-                            } else {
-                                Log_OC.e(TAG, "Error while synchronizing favourites : " + contentsResult.getLogMessage());
-                            }
-                        }
-                    }   // won't let these fails break the synchronization process
-                }
-
-                    
-                // removal of obsolete files
-                mChildren = mStorageManager.getDirectoryContent(mStorageManager.getFileById(mParentId));
-                OCFile file;
-                String currentSavePath = FileStorageUtils.getSavePath(mAccount.name);
-                for (int i=0; i < mChildren.size(); ) {
-                    file = mChildren.get(i);
-                    if (file.getLastSyncDateForProperties() != mCurrentSyncTime) {
-                        Log_OC.d(TAG, "removing file: " + file);
-                        mStorageManager.removeFile(file, (file.isDown() && file.getStoragePath().startsWith(currentSavePath)));
-                        mChildren.remove(i);
-                    } else {
-                        i++;
-                    }
-                }
-                
-            } else {
-                client.exhaustResponse(query.getResponseBodyAsStream());
-            }
-            
-            // prepare result object
-            if (isMultiStatus(status)) {
-                if (mConflictsFound > 0  || mFailsInFavouritesFound > 0) { 
-                    result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);   // should be different result, but will do the job
-                            
-                } else {
-                    result = new RemoteOperationResult(true, status, query.getResponseHeaders());
-                }
-            } else {
-                result = new RemoteOperationResult(false, status, query.getResponseHeaders());
-            }
-            
-            
-            
-        } catch (Exception e) {
-            result = new RemoteOperationResult(e);
-            
-
-        } finally {
-            if (query != null)
-                query.releaseConnection();  // let the connection available for other methods
-            if (result.isSuccess()) {
-                Log_OC.i(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage());
-            } else {
-                if (result.isException()) {
-                    Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage(), result.getException());
-                } else {
-                    Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage());
-                }
-            }
-        }
-        
-        return result;
-    }
-    
-
-    public boolean isMultiStatus(int status) {
-        return (status == HttpStatus.SC_MULTI_STATUS); 
-    }
-
-
-    /**
-     * Creates and populates a new {@link OCFile} object with the data read from the server.
-     * 
-     * @param we        WebDAV entry read from the server for a WebDAV resource (remote file or folder).
-     * @return          New OCFile instance representing the remote resource described by we.
-     */
-    private OCFile fillOCFile(WebdavEntry we) {
-        OCFile file = new OCFile(we.decodedPath());
-        file.setCreationTimestamp(we.createTimestamp());
-        file.setFileLength(we.contentLength());
-        file.setMimetype(we.contentType());
-        file.setModificationTimestamp(we.modifiedTimestamp());
-        file.setParentId(mParentId);
-        return file;
-    }
-    
-
-    /**
-     * Checks the storage path of the OCFile received as parameter. If it's out of the local ownCloud folder,
-     * tries to copy the file inside it. 
-     * 
-     * If the copy fails, the link to the local file is nullified. The account of forgotten files is kept in 
-     * {@link #mForgottenLocalFiles}
-     * 
-     * @param file      File to check and fix.
-     */
-    private void checkAndFixForeignStoragePath(OCFile file) {
-        String storagePath = file.getStoragePath();
-        String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, file);
-        if (storagePath != null && !storagePath.equals(expectedPath)) {
-            /// fix storagePaths out of the local ownCloud folder
-            File originalFile = new File(storagePath);
-            if (FileStorageUtils.getUsableSpace(mAccount.name) < originalFile.length()) {
-                mForgottenLocalFiles.put(file.getRemotePath(), storagePath);
-                file.setStoragePath(null);
-                    
-            } else {
-                InputStream in = null;
-                OutputStream out = null;
-                try {
-                    File expectedFile = new File(expectedPath);
-                    File expectedParent = expectedFile.getParentFile();
-                    expectedParent.mkdirs();
-                    if (!expectedParent.isDirectory()) {
-                        throw new IOException("Unexpected error: parent directory could not be created");
-                    }
-                    expectedFile.createNewFile();
-                    if (!expectedFile.isFile()) {
-                        throw new IOException("Unexpected error: target file could not be created");
-                    }                    
-                    in = new FileInputStream(originalFile);
-                    out = new FileOutputStream(expectedFile);
-                    byte[] buf = new byte[1024];
-                    int len;
-                    while ((len = in.read(buf)) > 0){
-                        out.write(buf, 0, len);
-                    }
-                    file.setStoragePath(expectedPath);
-                    
-                } catch (Exception e) {
-                    Log_OC.e(TAG, "Exception while copying foreign file " + expectedPath, e);
-                    mForgottenLocalFiles.put(file.getRemotePath(), storagePath);
-                    file.setStoragePath(null);
-                    
-                } finally {
-                    try {
-                        if (in != null) in.close();
-                    } catch (Exception 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_OC.d(TAG, "Weird exception while closing output stream for " + expectedPath + " (ignoring)", e);
-                    }
-                }
-            }
-        }
-    }
-
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/operations/UpdateOCVersionOperation.java b/src/de/mobilcom/debitel/cloud/android/operations/UpdateOCVersionOperation.java
deleted file mode 100644 (file)
index fdf7fca..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.operations;
-
-import org.apache.commons.httpclient.HttpStatus;
-import org.apache.commons.httpclient.methods.GetMethod;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.content.Context;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.authentication.AccountAuthenticator;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-import de.mobilcom.debitel.cloud.android.utils.OwnCloudVersion;
-
-import eu.alefzero.webdav.WebdavClient;
-
-/**
- * Remote operation that checks the version of an ownCloud server and stores it locally
- * 
- * @author David A. Velasco
- */
-public class UpdateOCVersionOperation extends RemoteOperation {
-
-    private static final String TAG = UpdateOCVersionOperation.class.getSimpleName();
-
-    private Account mAccount;
-    private Context mContext;
-    
-    
-    public UpdateOCVersionOperation(Account account, Context context) {
-        mAccount = account;
-        mContext = context;
-    }
-    
-    
-    @Override
-    protected RemoteOperationResult run(WebdavClient client) {
-        AccountManager accountMngr = AccountManager.get(mContext); 
-        String statUrl = accountMngr.getUserData(mAccount, AccountAuthenticator.KEY_OC_BASE_URL);
-        statUrl += AccountUtils.STATUS_PATH;
-        RemoteOperationResult result = null;
-        GetMethod get = null;
-        try {
-            get = new GetMethod(statUrl);
-            int status = client.executeMethod(get);
-            if (status != HttpStatus.SC_OK) {
-                client.exhaustResponse(get.getResponseBodyAsStream());
-                result = new RemoteOperationResult(false, status, get.getResponseHeaders());
-                
-            } else {
-                String response = get.getResponseBodyAsString();
-                if (response != null) {
-                    JSONObject json = new JSONObject(response);
-                    if (json != null && json.getString("version") != null) {
-                        OwnCloudVersion ocver = new OwnCloudVersion(json.getString("version"));
-                        if (ocver.isVersionValid()) {
-                            accountMngr.setUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION, ocver.toString());
-                            Log_OC.d(TAG, "Got new OC version " + ocver.toString());
-                            result = new RemoteOperationResult(ResultCode.OK);
-                            
-                        } else {
-                            Log_OC.w(TAG, "Invalid version number received from server: " + json.getString("version"));
-                            result = new RemoteOperationResult(RemoteOperationResult.ResultCode.BAD_OC_VERSION);
-                        }
-                    }
-                }
-                if (result == null) {
-                    result = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
-                }
-            }
-            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_OC.e(TAG, "Check for update of ownCloud server version at " + client.getBaseUri() + ": " + result.getLogMessage(), e);
-                
-        } catch (Exception e) {
-            result = new RemoteOperationResult(e);
-            Log_OC.e(TAG, "Check for update of ownCloud server version at " + client.getBaseUri() + ": " + result.getLogMessage(), e);
-            
-        } finally {
-            if (get != null) 
-                get.releaseConnection();
-        }
-        return result;
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/operations/UploadFileOperation.java b/src/de/mobilcom/debitel/cloud/android/operations/UploadFileOperation.java
deleted file mode 100644 (file)
index 5a5a1e4..0000000
+++ /dev/null
@@ -1,428 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.operations;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.HashSet;
-import java.util.Set;
-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 android.accounts.Account;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader;
-import de.mobilcom.debitel.cloud.android.network.ProgressiveDataTransferer;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-import de.mobilcom.debitel.cloud.android.utils.FileStorageUtils;
-
-import eu.alefzero.webdav.FileRequestEntity;
-import eu.alefzero.webdav.OnDatatransferProgressListener;
-import eu.alefzero.webdav.WebdavClient;
-import eu.alefzero.webdav.WebdavUtils;
-
-/**
- * Remote operation performing the upload of a file to an ownCloud server
- * 
- * @author David A. Velasco
- */
-public class UploadFileOperation extends RemoteOperation {
-
-    private static final String TAG = UploadFileOperation.class.getSimpleName();
-
-    private Account mAccount;
-    private OCFile mFile;
-    private OCFile mOldFile;
-    private String mRemotePath = null;
-    private boolean mIsInstant = false;
-    private boolean mRemoteFolderToBeCreated = false;
-    private boolean mForceOverwrite = false;
-    private int mLocalBehaviour = FileUploader.LOCAL_BEHAVIOUR_COPY;
-    private boolean mWasRenamed = false;
-    private String mOriginalFileName = null;
-    private String mOriginalStoragePath = null;
-    PutMethod mPutMethod = null;
-    private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
-    private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
-
-    protected RequestEntity mEntity = null;
-
-    
-    public UploadFileOperation( Account account,
-                                OCFile file,
-                                boolean isInstant, 
-                                boolean forceOverwrite,
-                                int localBehaviour) {
-        if (account == null)
-            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());
-        }
-
-        mAccount = account;
-        mFile = file;
-        mRemotePath = file.getRemotePath();
-        mIsInstant = isInstant;
-        mForceOverwrite = forceOverwrite;
-        mLocalBehaviour = localBehaviour;
-        mOriginalStoragePath = mFile.getStoragePath();
-        mOriginalFileName = mFile.getFileName();
-    }
-
-    public Account getAccount() {
-        return mAccount;
-    }
-
-    public String getFileName() {
-        return mOriginalFileName;
-    }
-
-    public OCFile getFile() {
-        return mFile;
-    }
-
-    public OCFile getOldFile() {
-        return mOldFile;
-    }
-
-    public String getOriginalStoragePath() {
-        return mOriginalStoragePath;
-    }
-
-    public String getStoragePath() {
-        return mFile.getStoragePath();
-    }
-
-    public String getRemotePath() {
-        return mFile.getRemotePath();
-    }
-
-    public String getMimeType() {
-        return mFile.getMimetype();
-    }
-
-    public boolean isInstant() {
-        return mIsInstant;
-    }
-
-    public boolean isRemoteFolderToBeCreated() {
-        return mRemoteFolderToBeCreated;
-    }
-
-    public void setRemoteFolderToBeCreated() {
-        mRemoteFolderToBeCreated = true;
-    }
-
-    public boolean getForceOverwrite() {
-        return mForceOverwrite;
-    }
-
-    public boolean wasRenamed() {
-        return mWasRenamed;
-    }
-
-    public Set<OnDatatransferProgressListener> getDataTransferListeners() {
-        return mDataTransferListeners;
-    }
-    
-    public void addDatatransferProgressListener (OnDatatransferProgressListener 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
-            if (!mForceOverwrite) {
-                String remotePath = getAvailableRemotePath(client, mRemotePath);
-                mWasRenamed = !remotePath.equals(mRemotePath);
-                if (mWasRenamed) {
-                    createNewOCFile(remotePath);
-                }
-            }
-            nameCheckPassed = true;
-
-            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)
-            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
-
-                } 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
-                        InputStream in = null;
-                        OutputStream out = null;
-                        try {
-                            File temporalParent = temporalFile.getParentFile();
-                            temporalParent.mkdirs();
-                            if (!temporalParent.isDirectory()) {
-                                throw new IOException("Unexpected error: parent directory could not be created");
-                            }
-                            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) {
-                                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();
-                            } catch (Exception e) {
-                                Log_OC.d(TAG, "Weird exception while closing input stream for " + mOriginalStoragePath + " (ignoring)", e);
-                            }
-                            try {
-                                if (out != null)
-                                    out.close();
-                            } catch (Exception e) {
-                                Log_OC.d(TAG, "Weird exception while closing output stream for " + expectedPath + " (ignoring)", e);
-                            }
-                        }
-                    }
-                }
-            }
-            localCopyPassed = true;
-
-            // / perform the upload
-            synchronized (mCancellationRequested) {
-                if (mCancellationRequested.get()) {
-                    throw new OperationCancelledException();
-                } else {
-                    mPutMethod = new PutMethod(client.getBaseUri() + WebdavUtils.encodePath(mFile.getRemotePath()));
-                }
-            }
-            int status = uploadFile(client);
-
-            // / 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
-                        fileToMove = temporalFile;
-                    } else { // FileUploader.LOCAL_BEHAVIOUR_MOVE
-                        fileToMove = originalFile;
-                    }
-                    if (!expectedFile.equals(fileToMove)) {
-                        File expectedFolder = expectedFile.getParentFile();
-                        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 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, (mPutMethod != null ? mPutMethod.getResponseHeaders() : null));
-
-        } catch (Exception e) {
-            // TODO something cleaner with cancellations
-            if (mCancellationRequested.get()) {
-                result = new RemoteOperationResult(new OperationCancelledException());
-            } else {
-                result = new RemoteOperationResult(e);
-            }
-
-        } finally {
-            if (temporalFile != null && !originalFile.equals(temporalFile)) {
-                temporalFile.delete();
-            }
-            if (result.isSuccess()) {
-                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)
-                                + ")";
-                    }
-                    Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage() + complement, result.getException());
-                } else {
-                    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);
-        newFile.setCreationTimestamp(mFile.getCreationTimestamp());
-        newFile.setFileLength(mFile.getFileLength());
-        newFile.setMimetype(mFile.getMimetype());
-        newFile.setModificationTimestamp(mFile.getModificationTimestamp());
-        newFile.setModificationTimestampAtLastSyncForData(mFile.getModificationTimestampAtLastSyncForData());
-        // newFile.setEtag(mFile.getEtag())
-        newFile.setKeepInSync(mFile.keepInSync());
-        newFile.setLastSyncDateForProperties(mFile.getLastSyncDateForProperties());
-        newFile.setLastSyncDateForData(mFile.getLastSyncDateForData());
-        newFile.setStoragePath(mFile.getStoragePath());
-        newFile.setParentId(mFile.getParentId());
-        mOldFile = mFile;
-        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());
-            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
-        }
-        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.
-     * 
-     * @param string
-     * @return
-     */
-    private String getAvailableRemotePath(WebdavClient wc, String remotePath) throws Exception {
-        boolean check = wc.existsFile(remotePath);
-        if (!check) {
-            return remotePath;
-        }
-
-        int pos = remotePath.lastIndexOf(".");
-        String suffix = "";
-        String extension = "";
-        if (pos >= 0) {
-            extension = remotePath.substring(pos + 1);
-            remotePath = remotePath.substring(0, pos);
-        }
-        int count = 2;
-        do {
-            suffix = " (" + count + ")";
-            if (pos >= 0)
-                check = wc.existsFile(remotePath + suffix + "." + extension);
-            else
-                check = wc.existsFile(remotePath + suffix);
-            count++;
-        } while (check);
-
-        if (pos >= 0) {
-            return remotePath + suffix + "." + extension;
-        } else {
-            return remotePath + suffix;
-        }
-    }
-
-    public void cancel() {
-        synchronized (mCancellationRequested) {
-            mCancellationRequested.set(true);
-            if (mPutMethod != null)
-                mPutMethod.abort();
-        }
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/providers/FileContentProvider.java b/src/de/mobilcom/debitel/cloud/android/providers/FileContentProvider.java
deleted file mode 100644 (file)
index af31e66..0000000
+++ /dev/null
@@ -1,301 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.providers;
-
-import java.util.HashMap;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.db.ProviderMeta;
-import de.mobilcom.debitel.cloud.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 UriMatcher mUriMatcher;
-//    static {
-//        mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
-//        mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, null, 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());
-        
-        String authority = getContext().getResources().getString(R.string.authority);
-        mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
-        mUriMatcher.addURI(authority, null, ROOT_DIRECTORY);
-        mUriMatcher.addURI(authority, "file/", SINGLE_FILE);
-        mUriMatcher.addURI(authority, "file/#", SINGLE_FILE);
-        mUriMatcher.addURI(authority, "dir/#", DIRECTORY);
-        
-        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();
-        // DB case_sensitive
-        db.execSQL("PRAGMA case_sensitive_like = true");
-        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);
-        }
-
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/syncadapter/AbstractOwnCloudSyncAdapter.java b/src/de/mobilcom/debitel/cloud/android/syncadapter/AbstractOwnCloudSyncAdapter.java
deleted file mode 100644 (file)
index 1d4d4fb..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-/* 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 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 de.mobilcom.debitel.cloud.android.syncadapter;\r
-\r
-import java.io.IOException;\r
-import java.util.Date;\r
-\r
-import org.apache.http.HttpRequest;\r
-import org.apache.http.HttpResponse;\r
-import org.apache.http.client.ClientProtocolException;\r
-import org.apache.http.conn.ConnectionKeepAliveStrategy;\r
-import org.apache.http.protocol.HttpContext;\r
-\r
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;\r
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils.AccountNotFoundException;\r
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;\r
-import de.mobilcom.debitel.cloud.android.network.OwnCloudClientUtils;\r
-\r
-import android.accounts.Account;\r
-import android.accounts.AccountManager;\r
-import android.accounts.AuthenticatorException;\r
-import android.accounts.OperationCanceledException;\r
-import android.content.AbstractThreadedSyncAdapter;\r
-import android.content.ContentProviderClient;\r
-import android.content.Context;\r
-import eu.alefzero.webdav.WebdavClient;\r
-\r
-/**\r
- * Base SyncAdapter for OwnCloud Designed to be subclassed for the concrete\r
- * SyncAdapter, like ConcatsSync, CalendarSync, FileSync etc..\r
- * \r
- * @author sassman\r
- * \r
- */\r
-public abstract class AbstractOwnCloudSyncAdapter extends\r
-        AbstractThreadedSyncAdapter {\r
-\r
-    private AccountManager accountManager;\r
-    private Account account;\r
-    private ContentProviderClient contentProvider;\r
-    private Date lastUpdated;\r
-    private DataStorageManager mStoreManager;\r
-\r
-    private WebdavClient mClient = null;\r
-\r
-    public AbstractOwnCloudSyncAdapter(Context context, boolean autoInitialize) {\r
-        super(context, autoInitialize);\r
-        this.setAccountManager(AccountManager.get(context));\r
-    }\r
-\r
-    public AccountManager getAccountManager() {\r
-        return accountManager;\r
-    }\r
-\r
-    public void setAccountManager(AccountManager accountManager) {\r
-        this.accountManager = accountManager;\r
-    }\r
-\r
-    public Account getAccount() {\r
-        return account;\r
-    }\r
-\r
-    public void setAccount(Account account) {\r
-        this.account = account;\r
-    }\r
-\r
-    public ContentProviderClient getContentProvider() {\r
-        return contentProvider;\r
-    }\r
-\r
-    public void setContentProvider(ContentProviderClient contentProvider) {\r
-        this.contentProvider = contentProvider;\r
-    }\r
-\r
-    public Date getLastUpdated() {\r
-        return lastUpdated;\r
-    }\r
-\r
-    public void setLastUpdated(Date lastUpdated) {\r
-        this.lastUpdated = lastUpdated;\r
-    }\r
-\r
-    public void setStorageManager(DataStorageManager storage_manager) {\r
-        mStoreManager = storage_manager;\r
-    }\r
-\r
-    public DataStorageManager getStorageManager() {\r
-        return mStoreManager;\r
-    }\r
-\r
-    protected ConnectionKeepAliveStrategy getKeepAliveStrategy() {\r
-        return new ConnectionKeepAliveStrategy() {\r
-            public long getKeepAliveDuration(HttpResponse response,\r
-                    HttpContext context) {\r
-                // Change keep alive straategy basing on response: ie\r
-                // forbidden/not found/etc\r
-                // should have keep alive 0\r
-                // default return: 5s\r
-                int statusCode = response.getStatusLine().getStatusCode();\r
-\r
-                // HTTP 400, 500 Errors as well as HTTP 118 - Connection timed\r
-                // out\r
-                if ((statusCode >= 400 && statusCode <= 418)\r
-                        || (statusCode >= 421 && statusCode <= 426)\r
-                        || (statusCode >= 500 && statusCode <= 510)\r
-                        || statusCode == 118) {\r
-                    return 0;\r
-                }\r
-\r
-                return 5 * 1000;\r
-            }\r
-        };\r
-    }\r
-\r
-    protected HttpResponse fireRawRequest(HttpRequest query)\r
-            throws ClientProtocolException, OperationCanceledException,\r
-            AuthenticatorException, IOException {\r
-        /*\r
-         * BasicHttpContext httpContext = new BasicHttpContext(); BasicScheme\r
-         * basicAuth = new BasicScheme();\r
-         * httpContext.setAttribute("preemptive-auth", basicAuth);\r
-         * \r
-         * HttpResponse response = getClient().execute(mHost, query,\r
-         * httpContext);\r
-         */\r
-        return null;\r
-    }\r
-\r
-    protected void initClientForCurrentAccount() throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException {\r
-        AccountUtils.constructFullURLForAccount(getContext(), account);\r
-        mClient = OwnCloudClientUtils.createOwnCloudClient(account, getContext());\r
-    }\r
-    \r
-    protected WebdavClient getClient() {\r
-        return mClient;\r
-    }\r
-}
\ No newline at end of file
diff --git a/src/de/mobilcom/debitel/cloud/android/syncadapter/ContactSyncAdapter.java b/src/de/mobilcom/debitel/cloud/android/syncadapter/ContactSyncAdapter.java
deleted file mode 100644 (file)
index ff2e84a..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.syncadapter;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-
-import org.apache.http.client.methods.HttpPut;
-import org.apache.http.entity.ByteArrayEntity;
-
-import de.mobilcom.debitel.cloud.android.authentication.AccountAuthenticator;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.AuthenticatorException;
-import android.accounts.OperationCanceledException;
-import android.content.ContentProviderClient;
-import android.content.Context;
-import android.content.SyncResult;
-import android.content.res.AssetFileDescriptor;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.ContactsContract;
-
-public class ContactSyncAdapter extends AbstractOwnCloudSyncAdapter {
-    private String mAddrBookUri;
-
-    public ContactSyncAdapter(Context context, boolean autoInitialize) {
-        super(context, autoInitialize);
-        mAddrBookUri = null;
-    }
-
-    @Override
-    public void onPerformSync(Account account, Bundle extras, String authority,
-            ContentProviderClient provider, SyncResult syncResult) {
-        setAccount(account);
-        setContentProvider(provider);
-        Cursor c = getLocalContacts(false);
-        if (c.moveToFirst()) {
-            do {
-                String lookup = c.getString(c
-                        .getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
-                String a = getAddressBookUri();
-                String uri = a + lookup + ".vcf";
-                FileInputStream f;
-                try {
-                    f = getContactVcard(lookup);
-                    HttpPut query = new HttpPut(uri);
-                    byte[] b = new byte[f.available()];
-                    f.read(b);
-                    query.setEntity(new ByteArrayEntity(b));
-                    fireRawRequest(query);
-                } catch (IOException e) {
-                    e.printStackTrace();
-                    return;
-                } catch (OperationCanceledException e) {
-                    // TODO Auto-generated catch block
-                    e.printStackTrace();
-                } catch (AuthenticatorException e) {
-                    // TODO Auto-generated catch block
-                    e.printStackTrace();
-                }
-            } while (c.moveToNext());
-            // } while (c.moveToNext());
-        }
-
-    }
-
-    private String getAddressBookUri() {
-        if (mAddrBookUri != null)
-            return mAddrBookUri;
-
-        AccountManager am = getAccountManager();
-        @SuppressWarnings("deprecation")
-        String uri = am.getUserData(getAccount(),
-                AccountAuthenticator.KEY_OC_URL).replace(
-                AccountUtils.WEBDAV_PATH_2_0, AccountUtils.CARDDAV_PATH_2_0);
-        uri += "/addressbooks/"
-                + getAccount().name.substring(0,
-                        getAccount().name.lastIndexOf('@')) + "/default/";
-        mAddrBookUri = uri;
-        return uri;
-    }
-
-    private FileInputStream getContactVcard(String lookupKey)
-            throws IOException {
-        Uri uri = Uri.withAppendedPath(
-                ContactsContract.Contacts.CONTENT_VCARD_URI, lookupKey);
-        AssetFileDescriptor fd = getContext().getContentResolver()
-                .openAssetFileDescriptor(uri, "r");
-        return fd.createInputStream();
-    }
-
-    private Cursor getLocalContacts(boolean include_hidden_contacts) {
-        return getContext().getContentResolver().query(
-                ContactsContract.Contacts.CONTENT_URI,
-                new String[] { ContactsContract.Contacts._ID,
-                        ContactsContract.Contacts.LOOKUP_KEY },
-                ContactsContract.Contacts.IN_VISIBLE_GROUP + " = ?",
-                new String[] { (include_hidden_contacts ? "0" : "1") },
-                ContactsContract.Contacts._ID + " DESC");
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/syncadapter/ContactSyncService.java b/src/de/mobilcom/debitel/cloud/android/syncadapter/ContactSyncService.java
deleted file mode 100644 (file)
index 9d15ca5..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.syncadapter;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-
-public class ContactSyncService extends Service {
-    private static final Object syncAdapterLock = new Object();
-    private static AbstractOwnCloudSyncAdapter mSyncAdapter = null;
-
-    @Override
-    public void onCreate() {
-        synchronized (syncAdapterLock) {
-            if (mSyncAdapter == null) {
-                mSyncAdapter = new ContactSyncAdapter(getApplicationContext(),
-                        true);
-            }
-        }
-    }
-
-    @Override
-    public IBinder onBind(Intent arg0) {
-        return mSyncAdapter.getSyncAdapterBinder();
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/syncadapter/FileSyncAdapter.java b/src/de/mobilcom/debitel/cloud/android/syncadapter/FileSyncAdapter.java
deleted file mode 100644 (file)
index e073971..0000000
+++ /dev/null
@@ -1,409 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.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 de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.authentication.AuthenticatorActivity;
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult;
-import de.mobilcom.debitel.cloud.android.operations.SynchronizeFolderOperation;
-import de.mobilcom.debitel.cloud.android.operations.UpdateOCVersionOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-import de.mobilcom.debitel.cloud.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!
-            
-            sendStickyBroadcast(true, remotePath, null);
-            
-        } else {
-            if (result.getCode() == RemoteOperationResult.ResultCode.UNAUTHORIZED ||
-                   // (result.isTemporalRedirection() && result.isIdPRedirection() &&
-                    ( result.isIdPRedirection() && 
-                            MainApp.getAuthTokenTypeSamlSessionCookie().equals(getClient().getAuthTokenType()))) {
-                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());
-                
-                // Update folder size on DB
-                getStorageManager().calculateFolderSize(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) {
-        FileSyncService fileSyncService = new FileSyncService();
-        
-        Intent i = new Intent(fileSyncService.getSyncMessage());
-        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 ||
-                                                // (mLastFailedResult.isTemporalRedirection() && mLastFailedResult.isIdPRedirection() && 
-                                                ( mLastFailedResult.isIdPRedirection() && 
-                                                 MainApp.getAuthTokenTypeSamlSessionCookie().equals(getClient().getAuthTokenType()))
-                                             )
-                                           );
-        // 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_ENFORCED_UPDATE, true);
-            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);
-        
-    }
-    
-    
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/syncadapter/FileSyncService.java b/src/de/mobilcom/debitel/cloud/android/syncadapter/FileSyncService.java
deleted file mode 100644 (file)
index de0f459..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/* 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 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
-package de.mobilcom.debitel.cloud.android.syncadapter;\r
-\r
-import android.app.Service;\r
-import android.content.Intent;\r
-import android.os.IBinder;\r
-\r
-/**\r
- * Background service for syncing files to our local Database\r
- * \r
- * @author Bartek Przybylski\r
- * \r
- */\r
-public class FileSyncService extends Service {\r
-    public static final String SYNC_MESSAGE = "ACCOUNT_SYNC";\r
-    public static final String SYNC_FOLDER_REMOTE_PATH = "SYNC_FOLDER_REMOTE_PATH";\r
-    public static final String IN_PROGRESS = "SYNC_IN_PROGRESS";\r
-    public static final String ACCOUNT_NAME = "ACCOUNT_NAME";\r
-    public static final String SYNC_RESULT = "SYNC_RESULT";\r
-\r
-    public String getSyncMessage(){\r
-        return getClass().getName().toString() + SYNC_MESSAGE;\r
-    }\r
-    /*\r
-     * {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void onCreate() {\r
-    }\r
-\r
-    /*\r
-     * {@inheritDoc}\r
-     */\r
-    @Override\r
-    public IBinder onBind(Intent intent) {\r
-       return new FileSyncAdapter(getApplicationContext(), true).getSyncAdapterBinder();\r
-    }\r
-}\r
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/ActionItem.java b/src/de/mobilcom/debitel/cloud/android/ui/ActionItem.java
deleted file mode 100644 (file)
index 6b90767..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/* 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 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
-package de.mobilcom.debitel.cloud.android.ui;\r
-\r
-import android.graphics.drawable.Drawable;\r
-import android.view.View.OnClickListener;\r
-\r
-/**\r
- * Represents an Item on the ActionBar.\r
- * \r
- * @author Bartek Przybylski\r
- * \r
- */\r
-public class ActionItem {\r
-    private Drawable mIcon;\r
-    private String mTitle;\r
-    private OnClickListener mClickListener;\r
-\r
-    public ActionItem() {\r
-    }\r
-\r
-    public void setTitle(String title) {\r
-        mTitle = title;\r
-    }\r
-\r
-    public String getTitle() {\r
-        return mTitle;\r
-    }\r
-\r
-    public void setIcon(Drawable icon) {\r
-        mIcon = icon;\r
-    }\r
-\r
-    public Drawable getIcon() {\r
-        return mIcon;\r
-    }\r
-\r
-    public void setOnClickListener(OnClickListener listener) {\r
-        mClickListener = listener;\r
-    }\r
-\r
-    public OnClickListener getOnClickListerner() {\r
-        return mClickListener;\r
-    }\r
-\r
-}\r
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/CustomButton.java b/src/de/mobilcom/debitel/cloud/android/ui/CustomButton.java
deleted file mode 100644 (file)
index 09b8215..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-package de.mobilcom.debitel.cloud.android.ui;
-
-import de.mobilcom.debitel.cloud.android.R;
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.Button;
-/**
- * @author masensio
- *
- * Button for customizing the button background
- */
-
-public class CustomButton extends Button {
-    
-    public CustomButton(Context context) {
-        super(context);
-        
-        boolean customButtons = getResources().getBoolean(R.bool.custom_buttons);
-        if (customButtons)
-        {
-            this.setBackgroundResource(R.drawable.btn_default);
-        }
-    }
-
-    public CustomButton(Context context, AttributeSet attrs) {
-        super(context, attrs);
-
-        boolean customButtons = getResources().getBoolean(R.bool.custom_buttons);
-        if (customButtons)
-        {
-            this.setBackgroundResource(R.drawable.btn_default);
-        }
-    }
-
-    public CustomButton(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        
-        boolean customButtons = getResources().getBoolean(R.bool.custom_buttons);
-        if (customButtons)
-        {
-            this.setBackgroundResource(R.drawable.btn_default);
-        }
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/CustomPopup.java b/src/de/mobilcom/debitel/cloud/android/ui/CustomPopup.java
deleted file mode 100644 (file)
index 244a193..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-/* 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 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
-package de.mobilcom.debitel.cloud.android.ui;\r
-\r
-import android.content.Context;\r
-import android.graphics.Rect;\r
-import android.graphics.drawable.BitmapDrawable;\r
-import android.graphics.drawable.Drawable;\r
-import android.view.Gravity;\r
-import android.view.LayoutInflater;\r
-import android.view.MotionEvent;\r
-import android.view.View;\r
-import android.view.WindowManager;\r
-import android.view.View.OnTouchListener;\r
-import android.view.ViewGroup.LayoutParams;\r
-import android.widget.PopupWindow;\r
-\r
-/**\r
- * Represents a custom PopupWindows\r
- * \r
- * @author Lorensius. W. T\r
- * \r
- */\r
-public class CustomPopup {\r
-    protected final View mAnchor;\r
-    protected final PopupWindow mWindow;\r
-    private View root;\r
-    private Drawable background = null;\r
-    protected final WindowManager mWManager;\r
-\r
-    public CustomPopup(View anchor) {\r
-        mAnchor = anchor;\r
-        mWindow = new PopupWindow(anchor.getContext());\r
-\r
-        mWindow.setTouchInterceptor(new OnTouchListener() {\r
-\r
-            public boolean onTouch(View v, MotionEvent event) {\r
-                if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {\r
-                    CustomPopup.this.dismiss();\r
-                    return true;\r
-                }\r
-                return false;\r
-            }\r
-        });\r
-\r
-        mWManager = (WindowManager) anchor.getContext().getSystemService(\r
-                Context.WINDOW_SERVICE);\r
-        onCreate();\r
-    }\r
-\r
-    public void onCreate() {\r
-    }\r
-\r
-    public void onShow() {\r
-    }\r
-\r
-    public void preShow() {\r
-        if (root == null) {\r
-            throw new IllegalStateException(\r
-                    "setContentView called with a view to display");\r
-        }\r
-\r
-        onShow();\r
-\r
-        if (background == null) {\r
-            mWindow.setBackgroundDrawable(new BitmapDrawable());\r
-        } else {\r
-            mWindow.setBackgroundDrawable(background);\r
-        }\r
-\r
-        mWindow.setWidth(WindowManager.LayoutParams.WRAP_CONTENT);\r
-        mWindow.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);\r
-        mWindow.setTouchable(true);\r
-        mWindow.setFocusable(true);\r
-        mWindow.setOutsideTouchable(true);\r
-\r
-        mWindow.setContentView(root);\r
-    }\r
-\r
-    public void setBackgroundDrawable(Drawable background) {\r
-        this.background = background;\r
-    }\r
-\r
-    public void setContentView(View root) {\r
-        this.root = root;\r
-        mWindow.setContentView(root);\r
-    }\r
-\r
-    public void setContentView(int layoutResId) {\r
-        LayoutInflater inflater = (LayoutInflater) mAnchor.getContext()\r
-                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);\r
-        setContentView(inflater.inflate(layoutResId, null));\r
-    }\r
-\r
-    public void showDropDown() {\r
-        showDropDown(0, 0);\r
-    }\r
-\r
-    public void showDropDown(int x, int y) {\r
-        preShow();\r
-        mWindow.setAnimationStyle(android.R.style.Animation_Dialog);\r
-        mWindow.showAsDropDown(mAnchor, x, y);\r
-    }\r
-\r
-    public void showLikeQuickAction() {\r
-        showLikeQuickAction(0, 0);\r
-    }\r
-\r
-    public void showLikeQuickAction(int x, int y) {\r
-        preShow();\r
-\r
-        mWindow.setAnimationStyle(android.R.style.Animation_Dialog);\r
-        int[] location = new int[2];\r
-        mAnchor.getLocationOnScreen(location);\r
-\r
-        Rect anchorRect = new Rect(location[0], location[1], location[0]\r
-                + mAnchor.getWidth(), location[1] + mAnchor.getHeight());\r
-\r
-        root.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,\r
-                LayoutParams.WRAP_CONTENT));\r
-        root.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);\r
-\r
-        int rootW = root.getWidth(), rootH = root.getHeight();\r
-        int screenW = mWManager.getDefaultDisplay().getWidth();\r
-\r
-        int xpos = ((screenW - rootW) / 2) + x;\r
-        int ypos = anchorRect.top - rootH + y;\r
-\r
-        if (rootH > anchorRect.top) {\r
-            ypos = anchorRect.bottom + y;\r
-        }\r
-        mWindow.showAtLocation(mAnchor, Gravity.NO_GRAVITY, xpos, ypos);\r
-    }\r
-\r
-    public void dismiss() {\r
-        mWindow.dismiss();\r
-    }\r
-\r
-}\r
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/ExtendedListView.java b/src/de/mobilcom/debitel/cloud/android/ui/ExtendedListView.java
deleted file mode 100644 (file)
index 15a797f..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.util.AttributeSet;
-import android.widget.ListView;
-
-/**
- * ListView allowing to specify the position of an item that should be centered in the visible area, if possible.
- * 
- * The cleanest way I found to overcome the problem due to getHeight() returns 0 until the view is really drawn. 
- *  
- * @author David A. Velasco
- */
-public class ExtendedListView extends ListView {
-
-    private int mPositionToSetAndCenter;
-
-    public ExtendedListView(Context context) {
-        super(context);
-    }
-
-    public ExtendedListView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public ExtendedListView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * 
-     */
-    @Override
-    protected void onDraw (Canvas canvas) {
-        super.onDraw(canvas);
-        if (mPositionToSetAndCenter > 0) {
-            this.setSelectionFromTop(mPositionToSetAndCenter, getHeight() / 2);
-            mPositionToSetAndCenter = 0;
-        }
-    }
-    
-    /**
-     * Public method to set the position of the item that should be centered in the visible area of the view.
-     * 
-     * The position is saved here and checked in onDraw().
-     *  
-     * @param position         Position (in the list of items) of the item to center in the visible area.     
-     */
-    public void setAndCenterSelection(int position) {
-        mPositionToSetAndCenter = position;
-    }
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/QuickAction.java b/src/de/mobilcom/debitel/cloud/android/ui/QuickAction.java
deleted file mode 100644 (file)
index 5240839..0000000
+++ /dev/null
@@ -1,305 +0,0 @@
-/* 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 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
-package de.mobilcom.debitel.cloud.android.ui;\r
-\r
-import android.content.Context;\r
-\r
-import android.graphics.Rect;\r
-import android.graphics.drawable.Drawable;\r
-\r
-import android.widget.ImageView;\r
-import android.widget.TextView;\r
-import android.widget.LinearLayout;\r
-import android.widget.ScrollView;\r
-\r
-import android.view.Gravity;\r
-import android.view.LayoutInflater;\r
-import android.view.View;\r
-import android.view.View.OnClickListener;\r
-import android.view.ViewGroup.LayoutParams;\r
-import android.view.ViewGroup;\r
-\r
-import java.util.ArrayList;\r
-\r
-import de.mobilcom.debitel.cloud.android.R;\r
-\r
-/**\r
- * Popup window, shows action list as icon and text like the one in Gallery3D\r
- * app.\r
- * \r
- * @author Lorensius. W. T\r
- */\r
-public class QuickAction extends CustomPopup {\r
-    private final View root;\r
-    private final ImageView mArrowUp;\r
-    private final ImageView mArrowDown;\r
-    private final LayoutInflater inflater;\r
-    private final Context context;\r
-\r
-    protected static final int ANIM_GROW_FROM_LEFT = 1;\r
-    protected static final int ANIM_GROW_FROM_RIGHT = 2;\r
-    protected static final int ANIM_GROW_FROM_CENTER = 3;\r
-    protected static final int ANIM_REFLECT = 4;\r
-    protected static final int ANIM_AUTO = 5;\r
-\r
-    private int animStyle;\r
-    private ViewGroup mTrack;\r
-    private ScrollView scroller;\r
-    private ArrayList<ActionItem> actionList;\r
-\r
-    /**\r
-     * Constructor\r
-     * \r
-     * @param anchor {@link View} on where the popup window should be displayed\r
-     */\r
-    public QuickAction(View anchor) {\r
-        super(anchor);\r
-\r
-        actionList = new ArrayList<ActionItem>();\r
-        context = anchor.getContext();\r
-        inflater = (LayoutInflater) context\r
-                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);\r
-\r
-        root = (ViewGroup) inflater.inflate(R.layout.popup, null);\r
-\r
-        mArrowDown = (ImageView) root.findViewById(R.id.arrow_down);\r
-        mArrowUp = (ImageView) root.findViewById(R.id.arrow_up);\r
-\r
-        setContentView(root);\r
-\r
-        mTrack = (ViewGroup) root.findViewById(R.id.tracks);\r
-        scroller = (ScrollView) root.findViewById(R.id.scroller);\r
-        animStyle = ANIM_AUTO;\r
-    }\r
-\r
-    /**\r
-     * Set animation style\r
-     * \r
-     * @param animStyle animation style, default is set to ANIM_AUTO\r
-     */\r
-    public void setAnimStyle(int animStyle) {\r
-        this.animStyle = animStyle;\r
-    }\r
-\r
-    /**\r
-     * Add action item\r
-     * \r
-     * @param action {@link ActionItem} object\r
-     */\r
-    public void addActionItem(ActionItem action) {\r
-        actionList.add(action);\r
-    }\r
-\r
-    /**\r
-     * Show popup window. Popup is automatically positioned, on top or bottom of\r
-     * anchor view.\r
-     * \r
-     */\r
-    public void show() {\r
-        preShow();\r
-\r
-        int xPos, yPos;\r
-\r
-        int[] location = new int[2];\r
-\r
-        mAnchor.getLocationOnScreen(location);\r
-\r
-        Rect anchorRect = new Rect(location[0], location[1], location[0]\r
-                + mAnchor.getWidth(), location[1] + mAnchor.getHeight());\r
-\r
-        createActionList();\r
-\r
-        root.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,\r
-                LayoutParams.WRAP_CONTENT));\r
-        root.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);\r
-\r
-        int rootHeight = root.getMeasuredHeight();\r
-        int rootWidth = root.getMeasuredWidth();\r
-\r
-        int screenWidth = mWManager.getDefaultDisplay().getWidth();\r
-        int screenHeight = mWManager.getDefaultDisplay().getHeight();\r
-\r
-        // automatically get X coord of popup (top left)\r
-        if ((anchorRect.left + rootWidth) > screenWidth) {\r
-            xPos = anchorRect.left - (rootWidth - mAnchor.getWidth());\r
-        } else {\r
-            if (mAnchor.getWidth() > rootWidth) {\r
-                xPos = anchorRect.centerX() - (rootWidth / 2);\r
-            } else {\r
-                xPos = anchorRect.left;\r
-            }\r
-        }\r
-\r
-        int dyTop = anchorRect.top;\r
-        int dyBottom = screenHeight - anchorRect.bottom;\r
-\r
-        boolean onTop = (dyTop > dyBottom) ? true : false;\r
-\r
-        if (onTop) {\r
-            if (rootHeight > dyTop) {\r
-                yPos = 15;\r
-                LayoutParams l = scroller.getLayoutParams();\r
-                l.height = dyTop - mAnchor.getHeight();\r
-            } else {\r
-                yPos = anchorRect.top - rootHeight;\r
-            }\r
-        } else {\r
-            yPos = anchorRect.bottom;\r
-\r
-            if (rootHeight > dyBottom) {\r
-                LayoutParams l = scroller.getLayoutParams();\r
-                l.height = dyBottom;\r
-            }\r
-        }\r
-\r
-        showArrow(((onTop) ? R.id.arrow_down : R.id.arrow_up),\r
-                anchorRect.centerX() - xPos);\r
-\r
-        setAnimationStyle(screenWidth, anchorRect.centerX(), onTop);\r
-\r
-        mWindow.showAtLocation(mAnchor, Gravity.NO_GRAVITY, xPos, yPos);\r
-    }\r
-\r
-    /**\r
-     * Set animation style\r
-     * \r
-     * @param screenWidth screen width\r
-     * @param requestedX distance from left edge\r
-     * @param onTop flag to indicate where the popup should be displayed. Set\r
-     *            TRUE if displayed on top of anchor view and vice versa\r
-     */\r
-    private void setAnimationStyle(int screenWidth, int requestedX,\r
-            boolean onTop) {\r
-        int arrowPos = requestedX - mArrowUp.getMeasuredWidth() / 2;\r
-\r
-        switch (animStyle) {\r
-        case ANIM_GROW_FROM_LEFT:\r
-            mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Left\r
-                    : R.style.Animations_PopDownMenu_Left);\r
-            break;\r
-\r
-        case ANIM_GROW_FROM_RIGHT:\r
-            mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Right\r
-                    : R.style.Animations_PopDownMenu_Right);\r
-            break;\r
-\r
-        case ANIM_GROW_FROM_CENTER:\r
-            mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Center\r
-                    : R.style.Animations_PopDownMenu_Center);\r
-            break;\r
-\r
-        case ANIM_REFLECT:\r
-            mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Reflect\r
-                    : R.style.Animations_PopDownMenu_Reflect);\r
-            break;\r
-\r
-        case ANIM_AUTO:\r
-            if (arrowPos <= screenWidth / 4) {\r
-                mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Left\r
-                        : R.style.Animations_PopDownMenu_Left);\r
-            } else if (arrowPos > screenWidth / 4\r
-                    && arrowPos < 3 * (screenWidth / 4)) {\r
-                mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Center\r
-                        : R.style.Animations_PopDownMenu_Center);\r
-            } else {\r
-                mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Right\r
-                        : R.style.Animations_PopDownMenu_Right);\r
-            }\r
-\r
-            break;\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Create action list\r
-     */\r
-    private void createActionList() {\r
-        View view;\r
-        String title;\r
-        Drawable icon;\r
-        OnClickListener listener;\r
-\r
-        for (int i = 0; i < actionList.size(); i++) {\r
-            title = actionList.get(i).getTitle();\r
-            icon = actionList.get(i).getIcon();\r
-            listener = actionList.get(i).getOnClickListerner();\r
-\r
-            view = getActionItem(title, icon, listener);\r
-\r
-            view.setFocusable(true);\r
-            view.setClickable(true);\r
-\r
-            mTrack.addView(view);\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Get action item {@link View}\r
-     * \r
-     * @param title action item title\r
-     * @param icon {@link Drawable} action item icon\r
-     * @param listener {@link View.OnClickListener} action item listener\r
-     * @return action item {@link View}\r
-     */\r
-    private View getActionItem(String title, Drawable icon,\r
-            OnClickListener listener) {\r
-        LinearLayout container = (LinearLayout) inflater.inflate(\r
-                R.layout.action_item, null);\r
-\r
-        ImageView img = (ImageView) container.findViewById(R.id.icon);\r
-        TextView text = (TextView) container.findViewById(R.id.title);\r
-\r
-        if (icon != null) {\r
-            img.setImageDrawable(icon);\r
-        }\r
-\r
-        if (title != null) {\r
-            text.setText(title);\r
-        }\r
-\r
-        if (listener != null) {\r
-            container.setOnClickListener(listener);\r
-        }\r
-\r
-        return container;\r
-    }\r
-\r
-    /**\r
-     * Show arrow\r
-     * \r
-     * @param whichArrow arrow type resource id\r
-     * @param requestedX distance from left screen\r
-     */\r
-    private void showArrow(int whichArrow, int requestedX) {\r
-        final View showArrow = (whichArrow == R.id.arrow_up) ? mArrowUp\r
-                : mArrowDown;\r
-        final View hideArrow = (whichArrow == R.id.arrow_up) ? mArrowDown\r
-                : mArrowUp;\r
-\r
-        final int arrowWidth = mArrowUp.getMeasuredWidth();\r
-\r
-        showArrow.setVisibility(View.VISIBLE);\r
-\r
-        ViewGroup.MarginLayoutParams param = (ViewGroup.MarginLayoutParams) showArrow\r
-                .getLayoutParams();\r
-\r
-        param.leftMargin = requestedX - arrowWidth / 2;\r
-\r
-        hideArrow.setVisibility(View.INVISIBLE);\r
-    }\r
-}
\ No newline at end of file
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/activity/AccountSelectActivity.java b/src/de/mobilcom/debitel/cloud/android/ui/activity/AccountSelectActivity.java
deleted file mode 100644 (file)
index 852f810..0000000
+++ /dev/null
@@ -1,274 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.activity;
-
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.AccountManagerCallback;
-import android.accounts.AccountManagerFuture;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Handler;
-import android.view.ContextMenu;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.widget.AdapterView.AdapterContextMenuInfo;
-import android.widget.CheckedTextView;
-import android.widget.ListView;
-import android.widget.SimpleAdapter;
-import android.widget.TextView;
-
-import com.actionbarsherlock.app.ActionBar;
-import com.actionbarsherlock.app.SherlockListActivity;
-import com.actionbarsherlock.view.Menu;
-import com.actionbarsherlock.view.MenuInflater;
-import com.actionbarsherlock.view.MenuItem;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.authentication.AccountAuthenticator;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-import de.mobilcom.debitel.cloud.android.authentication.AuthenticatorActivity;
-
-public class AccountSelectActivity extends SherlockListActivity implements
-        AccountManagerCallback<Boolean> {
-
-    private static final String  TAG = "AccountSelectActivity";
-    
-    private static final String PREVIOUS_ACCOUNT_KEY = "ACCOUNT";
-    
-    private final Handler mHandler = new Handler();
-    private Account mPreviousAccount = null;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        if (savedInstanceState != null) {
-            mPreviousAccount = savedInstanceState.getParcelable(PREVIOUS_ACCOUNT_KEY);
-        } else {
-            mPreviousAccount = AccountUtils.getCurrentOwnCloudAccount(this);
-        }
-        
-        ActionBar action_bar = getSupportActionBar();
-        action_bar.setDisplayShowTitleEnabled(true);
-        action_bar.setDisplayHomeAsUpEnabled(false);
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        populateAccountList();
-    }
-    
-    @Override
-    protected void onPause() {
-        super.onPause();
-        if (this.isFinishing()) {
-            Account current = AccountUtils.getCurrentOwnCloudAccount(this);
-            if ((mPreviousAccount == null && current != null) || 
-                (mPreviousAccount != null && !mPreviousAccount.equals(current))) {
-                /// the account set as default changed since this activity was created 
-            
-                // trigger synchronization
-                ContentResolver.cancelSync(null, MainApp.getAuthTokenType());
-                Bundle bundle = new Bundle();
-                bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
-                ContentResolver.requestSync(AccountUtils.getCurrentOwnCloudAccount(this), MainApp.getAuthTokenType(), bundle);
-                
-                // restart the main activity
-                Intent i = new Intent(this, FileDisplayActivity.class);
-                i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-                startActivity(i);
-            }
-        }
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        // Show Create Account if Multiaccount is enabled
-        if (getResources().getBoolean(R.bool.multiaccount_support)) {
-            MenuInflater inflater = getSherlock().getMenuInflater();
-            inflater.inflate(R.menu.account_picker, menu);
-        }
-        return true;
-    }
-
-    @Override
-    public void onCreateContextMenu(ContextMenu menu, View v,
-            ContextMenuInfo menuInfo) {
-        getMenuInflater().inflate(R.menu.account_picker_long_click, menu);
-        super.onCreateContextMenu(menu, v, menuInfo);
-    }
-
-    @Override
-    protected void onListItemClick(ListView l, View v, int position, long id) {
-        String accountName = ((TextView) v.findViewById(android.R.id.text1))
-                .getText().toString();
-        AccountUtils.setCurrentOwnCloudAccount(this, accountName);
-        finish();   // immediate exit
-    }
-
-    @Override
-    public boolean onMenuItemSelected(int featureId, MenuItem item) {
-        if (item.getItemId() == R.id.createAccount) {
-            /*Intent intent = new Intent(
-                    android.provider.Settings.ACTION_ADD_ACCOUNT);
-            intent.putExtra("authorities",
-                    new String[] { MainApp.getAuthTokenType() });
-            startActivity(intent);*/
-            AccountManager am = AccountManager.get(getApplicationContext());
-            am.addAccount(MainApp.getAccountType(), 
-                            null,
-                            null, 
-                            null, 
-                            this, 
-                            null,                        
-                            null);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Called when the user clicked on an item into the context menu created at 
-     * {@link #onCreateContextMenu(ContextMenu, View, ContextMenuInfo)} for every
-     * ownCloud {@link Account} , containing 'secondary actions' for them.
-     * 
-     * {@inheritDoc}}
-     */
-    @SuppressWarnings("unchecked")
-    @Override
-    public boolean onContextItemSelected(android.view.MenuItem item) {
-        AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
-        int index = info.position;
-        HashMap<String, String> map = null;
-        try {
-            map = (HashMap<String, String>) getListAdapter().getItem(index);
-        } catch (ClassCastException e) {
-            Log_OC.wtf(TAG, "getitem(index) from list adapter did not return hashmap, bailing out");
-            return false;
-        }
-        
-        String accountName = map.get("NAME");
-        AccountManager am = (AccountManager) getSystemService(ACCOUNT_SERVICE);
-        Account accounts[] = am.getAccountsByType(MainApp.getAccountType());
-        for (Account a : accounts) {
-            if (a.name.equals(accountName)) {
-                if (item.getItemId() == R.id.change_password) {
-                    Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
-                    updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, a);
-                    updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_TOKEN);
-                    startActivity(updateAccountCredentials);
-                    
-                } else if (item.getItemId() == R.id.delete_account) {
-                    am.removeAccount(a, this, mHandler);
-                }
-            }
-        }
-
-        return true;
-    }
-
-    private void populateAccountList() {
-        AccountManager am = (AccountManager) getSystemService(ACCOUNT_SERVICE);
-        Account accounts[] = am
-                .getAccountsByType(MainApp.getAccountType());
-        if (am.getAccountsByType(MainApp.getAccountType()).length == 0) {
-            // Show create account screen if there isn't any account
-            am.addAccount(MainApp.getAccountType(), 
-                    null,
-                    null, 
-                    null, 
-                    this, 
-                    null,                        
-                    null);
-        }
-        else {
-            LinkedList<HashMap<String, String>> ll = new LinkedList<HashMap<String, String>>();
-            for (Account a : accounts) {
-                HashMap<String, String> h = new HashMap<String, String>();
-                h.put("NAME", a.name);
-                h.put("VER",
-                        "ownCloud version: "
-                                + am.getUserData(a,
-                                        AccountAuthenticator.KEY_OC_VERSION));
-                ll.add(h);
-            }
-
-            setListAdapter(new AccountCheckedSimpleAdepter(this, ll,
-                    android.R.layout.simple_list_item_single_choice,
-                    new String[] { "NAME" }, new int[] { android.R.id.text1 }));
-            registerForContextMenu(getListView());
-        }
-    }
-
-    @Override
-    public void run(AccountManagerFuture<Boolean> future) {
-        if (future.isDone()) {
-            Account a = AccountUtils.getCurrentOwnCloudAccount(this);
-            String accountName = "";
-            if (a == null) {
-                Account[] accounts = AccountManager.get(this)
-                        .getAccountsByType(MainApp.getAccountType());
-                if (accounts.length != 0)
-                    accountName = accounts[0].name;
-                AccountUtils.setCurrentOwnCloudAccount(this, accountName);
-            }
-            populateAccountList();
-        }
-    }
-
-    private class AccountCheckedSimpleAdepter extends SimpleAdapter {
-        private Account mCurrentAccount;
-        private List<? extends Map<String, ?>> mPrivateData;
-
-        public AccountCheckedSimpleAdepter(Context context,
-                List<? extends Map<String, ?>> data, int resource,
-                String[] from, int[] to) {
-            super(context, data, resource, from, to);
-            mCurrentAccount = AccountUtils
-                    .getCurrentOwnCloudAccount(AccountSelectActivity.this);
-            mPrivateData = data;
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            View v = super.getView(position, convertView, parent);
-            CheckedTextView ctv = (CheckedTextView) v
-                    .findViewById(android.R.id.text1);
-            if (mPrivateData.get(position).get("NAME")
-                    .equals(mCurrentAccount.name)) {
-                ctv.setChecked(true);
-            }
-            return v;
-        }
-
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/activity/ConflictsResolveActivity.java b/src/de/mobilcom/debitel/cloud/android/ui/activity/ConflictsResolveActivity.java
deleted file mode 100644 (file)
index e87febb..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.activity;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader;
-import de.mobilcom.debitel.cloud.android.ui.dialog.ConflictsResolveDialog;
-import de.mobilcom.debitel.cloud.android.ui.dialog.ConflictsResolveDialog.Decision;
-import de.mobilcom.debitel.cloud.android.ui.dialog.ConflictsResolveDialog.OnConflictDecisionMadeListener;
-
-import android.content.Intent;
-import android.os.Bundle;
-
-/**
- * Wrapper activity which will be launched if keep-in-sync file will be modified by external
- * application. 
- * 
- * @author Bartek Przybylski
- * @author David A. Velasco
- */
-public class ConflictsResolveActivity extends FileActivity implements OnConflictDecisionMadeListener {
-
-    private String TAG = ConflictsResolveActivity.class.getSimpleName();
-    
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-    }
-
-    @Override
-    public void ConflictDecisionMade(Decision decision) {
-        Intent i = new Intent(getApplicationContext(), FileUploader.class);
-        
-        switch (decision) {
-            case CANCEL:
-                finish();
-                return;
-            case OVERWRITE:
-                i.putExtra(FileUploader.KEY_FORCE_OVERWRITE, true);
-                break;
-            case KEEP_BOTH:
-                i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);
-                break;
-            default:
-                Log_OC.wtf(TAG, "Unhandled conflict decision " + decision);
-                return;
-        }
-        i.putExtra(FileUploader.KEY_ACCOUNT, getAccount());
-        i.putExtra(FileUploader.KEY_FILE, getFile());
-        i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
-        
-        startService(i);
-        finish();
-    }
-
-    @Override
-    protected void onAccountSet(boolean stateWasRecovered) {
-        if (getAccount() != null) {
-            OCFile file = getFile();
-            if (getFile() == null) {
-                Log_OC.e(TAG, "No conflictive file received");
-                finish();
-            } else {
-                /// Check whether the 'main' OCFile handled by the Activity is contained in the current Account
-                DataStorageManager storageManager = new FileDataStorageManager(getAccount(), getContentResolver());
-                file = storageManager.getFileByPath(file.getRemotePath());   // file = null if not in the current Account
-                if (file != null) {
-                    setFile(file);
-                    ConflictsResolveDialog d = ConflictsResolveDialog.newInstance(file.getRemotePath(), this);
-                    d.showDialog(this);
-                    
-                } else {
-                    // account was changed to a different one - just finish
-                    finish();
-                }
-            }
-            
-        } else {
-            Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
-            finish();
-        }
-        
-    }
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/activity/ErrorsWhileCopyingHandlerActivity.java b/src/de/mobilcom/debitel/cloud/android/ui/activity/ErrorsWhileCopyingHandlerActivity.java
deleted file mode 100644 (file)
index 33ffc32..0000000
+++ /dev/null
@@ -1,277 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.activity;
-
-import java.io.File;
-import java.util.ArrayList;
-
-import android.accounts.Account;
-import android.content.Context;
-import android.content.Intent;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.Handler;
-import android.support.v4.app.DialogFragment;
-import android.text.method.ScrollingMovementMethod;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.ui.CustomButton;
-import de.mobilcom.debitel.cloud.android.ui.dialog.IndeterminateProgressDialog;
-import de.mobilcom.debitel.cloud.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. let them unlinked to the remote
- * files.
- * 
- * Shown when the error notification summarizing the list of errors is clicked by the user.
- * 
- * @author David A. Velasco
- */
-public class ErrorsWhileCopyingHandlerActivity  extends SherlockFragmentActivity implements OnClickListener {
-
-    private static final String TAG = ErrorsWhileCopyingHandlerActivity.class.getSimpleName();
-    
-    public static final String EXTRA_ACCOUNT = ErrorsWhileCopyingHandlerActivity.class.getCanonicalName() + ".EXTRA_ACCOUNT";
-    public static final String EXTRA_LOCAL_PATHS = ErrorsWhileCopyingHandlerActivity.class.getCanonicalName() + ".EXTRA_LOCAL_PATHS";
-    public static final String EXTRA_REMOTE_PATHS = ErrorsWhileCopyingHandlerActivity.class.getCanonicalName() + ".EXTRA_REMOTE_PATHS";
-
-    private static final String WAIT_DIALOG_TAG = "WAIT_DIALOG";
-    
-    protected Account mAccount;
-    protected FileDataStorageManager mStorageManager;
-    protected ArrayList<String> mLocalPaths;
-    protected ArrayList<String> mRemotePaths;
-    protected ArrayAdapter<String> mAdapter;
-    protected Handler mHandler;
-    private DialogFragment mCurrentDialog;
-    
-    /**
-     * {@link}
-     */
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        
-        /// read extra parameters in intent
-        Intent intent = getIntent();
-        mAccount = intent.getParcelableExtra(EXTRA_ACCOUNT);
-        mRemotePaths = intent.getStringArrayListExtra(EXTRA_REMOTE_PATHS);
-        mLocalPaths = intent.getStringArrayListExtra(EXTRA_LOCAL_PATHS);
-        mStorageManager = new FileDataStorageManager(mAccount, getContentResolver());
-        mHandler = new Handler();
-        if (mCurrentDialog != null) {
-            mCurrentDialog.dismiss();
-            mCurrentDialog = null;
-        }
-        
-        /// load generic layout
-        setContentView(R.layout.generic_explanation);
-        
-        /// customize text message
-        TextView textView = (TextView) findViewById(R.id.message);
-        String appName = getString(R.string.app_name);
-        String message = String.format(getString(R.string.sync_foreign_files_forgotten_explanation), appName, appName, appName, appName, mAccount.name);
-        textView.setText(message);
-        textView.setMovementMethod(new ScrollingMovementMethod());
-        
-        /// load the list of local and remote files that failed
-        ListView listView = (ListView) findViewById(R.id.list);
-        if (mLocalPaths != null && mLocalPaths.size() > 0) {
-            mAdapter = new ErrorsWhileCopyingListAdapter();
-            listView.setAdapter(mAdapter);
-        } else {
-            listView.setVisibility(View.GONE);
-            mAdapter = null;
-        }
-        
-        /// customize buttons
-        CustomButton cancelBtn = (CustomButton) findViewById(R.id.cancel);
-        CustomButton okBtn = (CustomButton) findViewById(R.id.ok);
-        
-        okBtn.setText(R.string.foreign_files_move);
-        cancelBtn.setOnClickListener(this);
-        okBtn.setOnClickListener(this);
-    }
-    
-    
-    /**
-     * Customized adapter, showing the local files as main text in two-lines list item and the remote files
-     * as the secondary text. 
-     * 
-     * @author David A. Velasco
-     */
-    public class ErrorsWhileCopyingListAdapter extends ArrayAdapter<String> {
-        
-        ErrorsWhileCopyingListAdapter() {
-            super(ErrorsWhileCopyingHandlerActivity.this, android.R.layout.two_line_list_item, android.R.id.text1, mLocalPaths);
-        }
-
-        @Override
-        public boolean isEnabled(int position) {
-            return false;
-        }
-        
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public View getView (int position, View convertView, ViewGroup parent) {
-            View view = convertView;
-            if (view == null) {
-                LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-                view = vi.inflate(android.R.layout.two_line_list_item, null);
-            }
-            if (view != null)  {
-                String localPath = getItem(position);
-                if (localPath != null) {
-                    TextView text1 = (TextView) view.findViewById(android.R.id.text1);
-                    if (text1 != null) {
-                        text1.setText(String.format(getString(R.string.foreign_files_local_text), localPath));
-                    }
-                }
-                if (mRemotePaths != null && mRemotePaths.size() > 0 && position >= 0 && position < mRemotePaths.size()) {
-                    TextView text2 = (TextView) view.findViewById(android.R.id.text2);
-                    String remotePath = mRemotePaths.get(position);
-                    if (text2 != null && remotePath != null) {
-                        text2.setText(String.format(getString(R.string.foreign_files_remote_text), remotePath));
-                    }
-                }
-            }
-            return view;
-        }
-    }
-
-
-    /**
-     * Listener method to perform the MOVE / CANCEL action available in this activity.
-     * 
-     * @param v     Clicked view (button MOVE or CANCEL)
-     */
-    @Override
-    public void onClick(View v) {
-        if (v.getId() == R.id.ok) {
-            /// perform movement operation in background thread
-            Log_OC.d(TAG, "Clicked MOVE, start movement");
-            new MoveFilesTask().execute();            
-            
-        } else if (v.getId() == R.id.cancel) {
-            /// just finish
-            Log_OC.d(TAG, "Clicked CANCEL, bye");
-            finish();
-            
-        } else {
-            Log_OC.e(TAG, "Clicked phantom button, id: " + v.getId());
-        }
-    }
-
-    
-    /**
-     * Asynchronous task performing the move of all the local files to the ownCloud folder.
-     * 
-     * @author David A. Velasco
-     */
-    private class MoveFilesTask extends AsyncTask<Void, Void, Boolean> {
-
-        /**
-         * Updates the UI before trying the movement
-         */
-        @Override
-        protected void onPreExecute () {
-            /// progress dialog and disable 'Move' button
-            mCurrentDialog = IndeterminateProgressDialog.newInstance(R.string.wait_a_moment, false);
-            mCurrentDialog.show(getSupportFragmentManager(), WAIT_DIALOG_TAG);
-            findViewById(R.id.ok).setEnabled(false);
-        }
-        
-        
-        /**
-         * Performs the movement
-         * 
-         * @return     'False' when the movement of any file fails.
-         */
-        @Override
-        protected Boolean doInBackground(Void... params) {
-            while (!mLocalPaths.isEmpty()) {
-                String currentPath = mLocalPaths.get(0);
-                File currentFile = new File(currentPath);
-                String expectedPath = FileStorageUtils.getSavePath(mAccount.name) + mRemotePaths.get(0);
-                File expectedFile = new File(expectedPath);
-
-                if (expectedFile.equals(currentFile) || currentFile.renameTo(expectedFile)) {
-                    // SUCCESS
-                    OCFile file = mStorageManager.getFileByPath(mRemotePaths.get(0));
-                    file.setStoragePath(expectedPath);
-                    mStorageManager.saveFile(file);
-                    mRemotePaths.remove(0);
-                    mLocalPaths.remove(0);
-                        
-                } else {
-                    // FAIL
-                    return false;   
-                }
-            }
-            return true;
-        }
-
-        /**
-         * Updates the activity UI after the movement of local files is tried.
-         * 
-         * If the movement was successful for all the files, finishes the activity immediately.
-         * 
-         * In other case, the list of remaining files is still available to retry the movement.
-         * 
-         * @param result      'True' when the movement was successful.
-         */
-        @Override
-        protected void onPostExecute(Boolean result) {
-            mAdapter.notifyDataSetChanged();
-            mCurrentDialog.dismiss();
-            mCurrentDialog = null;
-            findViewById(R.id.ok).setEnabled(true);
-            
-            if (result) {
-                // nothing else to do in this activity
-                Toast t = Toast.makeText(ErrorsWhileCopyingHandlerActivity.this, getString(R.string.foreign_files_success), Toast.LENGTH_LONG);
-                t.show();
-                finish();
-                
-            } else {
-                Toast t = Toast.makeText(ErrorsWhileCopyingHandlerActivity.this, getString(R.string.foreign_files_fail), Toast.LENGTH_LONG);
-                t.show();
-            }
-        }
-    }    
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/activity/FailedUploadActivity.java b/src/de/mobilcom/debitel/cloud/android/ui/activity/FailedUploadActivity.java
deleted file mode 100644 (file)
index eeb8b27..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.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.TextView;\r
-\r
-import de.mobilcom.debitel.cloud.android.R;\r
-import de.mobilcom.debitel.cloud.android.ui.CustomButton;\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
-        CustomButton closeBtn = (CustomButton) findViewById(R.id.failed_uploadactivity_close_button);\r
-        \r
-        closeBtn.setOnClickListener(new OnClickListener() {\r
-            @Override\r
-            public void onClick(View v) {\r
-                finish();\r
-            }\r
-        });\r
-    }\r
-}\r
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/activity/FileActivity.java b/src/de/mobilcom/debitel/cloud/android/ui/activity/FileActivity.java
deleted file mode 100644 (file)
index f85b5fc..0000000
+++ /dev/null
@@ -1,320 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.activity;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.AccountManagerCallback;
-import android.accounts.AccountManagerFuture;
-import android.accounts.OperationCanceledException;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.webkit.MimeTypeMap;
-
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-
-import eu.alefzero.webdav.WebdavUtils;
-
-/**
- * Activity with common behaviour for activities handling {@link OCFile}s in ownCloud {@link Account}s .
- * 
- * @author David A. Velasco
- */
-public abstract class FileActivity extends SherlockFragmentActivity {
-
-    public static final String EXTRA_FILE = "de.mobilcom.debitel.cloud.android.ui.activity.FILE";
-    public static final String EXTRA_ACCOUNT = "de.mobilcom.debitel.cloud.android.ui.activity.ACCOUNT";
-    public static final String EXTRA_WAITING_TO_PREVIEW = "de.mobilcom.debitel.cloud.android.ui.activity.WAITING_TO_PREVIEW";
-    
-    public static final String TAG = FileActivity.class.getSimpleName(); 
-    
-    
-    /** OwnCloud {@link Account} where the main {@link OCFile} handled by the activity is located. */
-    private Account mAccount;
-    
-    /** Main {@link OCFile} handled by the activity.*/
-    private OCFile mFile;
-    
-    /** Flag to signal that the activity will is finishing to enforce the creation of an ownCloud {@link Account} */
-    private boolean mRedirectingToSetupAccount = false;
-    
-    /** Flag to signal when the value of mAccount was set */ 
-    private boolean mAccountWasSet;
-    
-    /** Flag to signal when the value of mAccount was restored from a saved state */ 
-    private boolean mAccountWasRestored;
-
-    
-    /**
-     * Loads the ownCloud {@link Account} and main {@link OCFile} to be handled by the instance of 
-     * the {@link FileActivity}.
-     * 
-     * Grants that a valid ownCloud {@link Account} is associated to the instance, or that the user 
-     * is requested to create a new one.
-     */
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        Account account;
-        if(savedInstanceState != null) {
-            account = savedInstanceState.getParcelable(FileActivity.EXTRA_ACCOUNT);
-            mFile = savedInstanceState.getParcelable(FileActivity.EXTRA_FILE);
-        } else {
-            account = getIntent().getParcelableExtra(FileActivity.EXTRA_ACCOUNT);
-            mFile = getIntent().getParcelableExtra(FileActivity.EXTRA_FILE);
-        }
-
-        setAccount(account, savedInstanceState != null);
-    }
-
-    
-    /**
-     *  Since ownCloud {@link Account}s can be managed from the system setting menu, 
-     *  the existence of the {@link Account} associated to the instance must be checked 
-     *  every time it is restarted.
-     */
-    @Override
-    protected void onRestart() {
-        super.onRestart();
-        boolean validAccount = (mAccount != null && AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), mAccount.name));
-        if (!validAccount) {
-            swapToDefaultAccount();
-        }
-        
-    }
-
-    
-    @Override 
-    protected void onStart() {
-        super.onStart();
-        if (mAccountWasSet) {
-            onAccountSet(mAccountWasRestored);
-        }
-    }
-    
-    
-    /**
-     *  Sets and validates the ownCloud {@link Account} associated to the Activity. 
-     * 
-     *  If not valid, tries to swap it for other valid and existing ownCloud {@link Account}.
-     *  
-     *  POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}. 
-     * 
-     *  @param account          New {@link Account} to set.
-     *  @param savedAccount     When 'true', account was retrieved from a saved instance state.
-     */
-    private void setAccount(Account account, boolean savedAccount) {
-        Account oldAccount = mAccount;
-        boolean validAccount = (account != null && AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), account.name));
-        if (validAccount) {
-            mAccount = account;
-            mAccountWasSet = true;
-            mAccountWasRestored = (savedAccount || mAccount.equals(oldAccount));
-            
-        } else {
-            swapToDefaultAccount();
-        }
-    }
-
-    
-    /**
-     *  Tries to swap the current ownCloud {@link Account} for other valid and existing. 
-     * 
-     *  If no valid ownCloud {@link Account} exists, the the user is requested 
-     *  to create a new ownCloud {@link Account}.
-     *  
-     *  POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}.
-     *   
-     *  @return     'True' if the checked {@link Account} was valid.
-     */
-    private void swapToDefaultAccount() {
-        // default to the most recently used account
-        Account newAccount  = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext());
-        if (newAccount == null) {
-            /// no account available: force account creation
-            createFirstAccount();
-            mRedirectingToSetupAccount = true;
-            mAccountWasSet = false;
-            mAccountWasRestored = false;
-            
-        } else {
-            mAccountWasSet = true;
-            mAccountWasRestored = (newAccount.equals(mAccount));
-            mAccount = newAccount;
-        }
-    }
-
-
-    /**
-     * Launches the account creation activity. To use when no ownCloud account is available
-     */
-    private void createFirstAccount() {
-        AccountManager am = AccountManager.get(getApplicationContext());
-        am.addAccount(MainApp.getAccountType(), 
-                        null,
-                        null, 
-                        null, 
-                        this, 
-                        new AccountCreationCallback(),                        
-                        null);
-    }
-
-    
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        outState.putParcelable(FileActivity.EXTRA_FILE, mFile);
-        outState.putParcelable(FileActivity.EXTRA_ACCOUNT, mAccount);
-    }
-    
-    
-    /**
-     * Getter for the main {@link OCFile} handled by the activity.
-     * 
-     * @return  Main {@link OCFile} handled by the activity.
-     */
-    public OCFile getFile() {
-        return mFile;
-    }
-
-    
-    /**
-     * Setter for the main {@link OCFile} handled by the activity.
-     * 
-     * @param file  Main {@link OCFile} to be handled by the activity.
-     */
-    public void setFile(OCFile file) {
-        mFile = file;
-    }
-
-    
-    /**
-     * Getter for the ownCloud {@link Account} where the main {@link OCFile} handled by the activity is located.
-     * 
-     * @return  OwnCloud {@link Account} where the main {@link OCFile} handled by the activity is located.
-     */
-    public Account getAccount() {
-        return mAccount;
-    }
-
-    
-    /**
-     * @return  'True' when the Activity is finishing to enforce the setup of a new account.
-     */
-    protected boolean isRedirectingToSetupAccount() {
-        return mRedirectingToSetupAccount;
-    }
-    
-    
-    /**
-     * Helper class handling a callback from the {@link AccountManager} after the creation of
-     * a new ownCloud {@link Account} finished, successfully or not.
-     * 
-     * At this moment, only called after the creation of the first account.
-     * 
-     * @author David A. Velasco
-     */
-    public class AccountCreationCallback implements AccountManagerCallback<Bundle> {
-
-        @Override
-        public void run(AccountManagerFuture<Bundle> future) {
-            FileActivity.this.mRedirectingToSetupAccount = false;
-            boolean accountWasSet = false;
-            if (future != null) {
-                try {
-                    Bundle result;
-                    result = future.getResult();
-                    String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
-                    String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
-                    if (AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), name)) {
-                        setAccount(new Account(name, type), false);
-                        accountWasSet = true;
-                    }
-                } catch (OperationCanceledException e) {
-                    Log_OC.d(TAG, "Account creation canceled");
-                    
-                } catch (Exception e) {
-                    Log_OC.e(TAG, "Account creation finished in exception: ", e);
-                }
-                    
-            } else {
-                Log_OC.e(TAG, "Account creation callback with null bundle");
-            }
-            if (!accountWasSet) {
-                moveTaskToBack(true);
-            }
-        }
-        
-    }
-    
-    
-    /**
-     *  Called when the ownCloud {@link Account} associated to the Activity was just updated.
-     * 
-     *  Child classes must grant that state depending on the {@link Account} is updated.
-     */
-    protected abstract void onAccountSet(boolean stateWasRecovered);
-    
-    
-
-    public void openFile(OCFile file) {
-        if (file != null) {
-            String storagePath = file.getStoragePath();
-            String encodedStoragePath = WebdavUtils.encodePath(storagePath);
-            
-            Intent intentForSavedMimeType = new Intent(Intent.ACTION_VIEW);
-            intentForSavedMimeType.setDataAndType(Uri.parse("file://"+ encodedStoragePath), file.getMimetype());
-            intentForSavedMimeType.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-            
-            Intent intentForGuessedMimeType = null;
-            if (storagePath.lastIndexOf('.') >= 0) {
-                String guessedMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1));
-                if (guessedMimeType != null && !guessedMimeType.equals(file.getMimetype())) {
-                    intentForGuessedMimeType = new Intent(Intent.ACTION_VIEW);
-                    intentForGuessedMimeType.setDataAndType(Uri.parse("file://"+ encodedStoragePath), guessedMimeType);
-                    intentForGuessedMimeType.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-                }
-            }
-            
-            Intent chooserIntent = null;
-            if (intentForGuessedMimeType != null) {
-                chooserIntent = Intent.createChooser(intentForGuessedMimeType, getString(R.string.actionbar_open_with));
-            } else {
-                chooserIntent = Intent.createChooser(intentForSavedMimeType, getString(R.string.actionbar_open_with));
-            }
-            
-            startActivity(chooserIntent);
-            
-        } else {
-            Log_OC.wtf(TAG, "Trying to open a NULL OCFile");
-        }
-    }
-    
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/activity/FileDisplayActivity.java b/src/de/mobilcom/debitel/cloud/android/ui/activity/FileDisplayActivity.java
deleted file mode 100644 (file)
index c68783b..0000000
+++ /dev/null
@@ -1,1388 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.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.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.FragmentManager;
-import android.support.v4.app.FragmentTransaction;
-import android.util.Log;
-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.view.Menu;
-import com.actionbarsherlock.view.MenuInflater;
-import com.actionbarsherlock.view.MenuItem;
-import com.actionbarsherlock.view.Window;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.files.services.FileDownloader;
-import de.mobilcom.debitel.cloud.android.files.services.FileObserverService;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader;
-import de.mobilcom.debitel.cloud.android.files.services.FileDownloader.FileDownloaderBinder;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader.FileUploaderBinder;
-import de.mobilcom.debitel.cloud.android.operations.CreateFolderOperation;
-import de.mobilcom.debitel.cloud.android.operations.OnRemoteOperationListener;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult;
-import de.mobilcom.debitel.cloud.android.operations.RemoveFileOperation;
-import de.mobilcom.debitel.cloud.android.operations.RenameFileOperation;
-import de.mobilcom.debitel.cloud.android.operations.SynchronizeFileOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-import de.mobilcom.debitel.cloud.android.syncadapter.FileSyncService;
-import de.mobilcom.debitel.cloud.android.ui.dialog.EditNameDialog;
-import de.mobilcom.debitel.cloud.android.ui.dialog.LoadingDialog;
-import de.mobilcom.debitel.cloud.android.ui.dialog.SslValidatorDialog;
-import de.mobilcom.debitel.cloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
-import de.mobilcom.debitel.cloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener;
-import de.mobilcom.debitel.cloud.android.ui.fragment.FileDetailFragment;
-import de.mobilcom.debitel.cloud.android.ui.fragment.FileFragment;
-import de.mobilcom.debitel.cloud.android.ui.fragment.OCFileListFragment;
-import de.mobilcom.debitel.cloud.android.ui.preview.PreviewImageActivity;
-import de.mobilcom.debitel.cloud.android.ui.preview.PreviewMediaFragment;
-import de.mobilcom.debitel.cloud.android.ui.preview.PreviewVideoActivity;
-
-/**
- * Displays, what files the user has available in his ownCloud.
- * 
- * @author Bartek Przybylski
- * @author David A. Velasco
- */
-
-public class FileDisplayActivity extends FileActivity implements
-OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNavigationListener, OnSslValidatorListener, OnRemoteOperationListener, EditNameDialogListener {
-
-    private ArrayAdapter<String> mDirectories;
-
-    /** Access point to the cached database for the current ownCloud {@link Account} */
-    private DataStorageManager mStorageManager = null;
-
-    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 boolean mDualPane;
-    private View mLeftFragmentContainer;
-    private View mRightFragmentContainer;
-
-    private static final String KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW";
-
-    public static final int DIALOG_SHORT_WAIT = 0;
-    private static final int DIALOG_CHOOSE_UPLOAD_SOURCE = 1;
-    private static final int DIALOG_SSL_VALIDATOR = 2;
-    private static final int DIALOG_CERT_NOT_SAVED = 3;
-    
-    private static final String DIALOG_WAIT_TAG = "DIALOG_WAIT";
-
-    public static final String ACTION_DETAILS = "de.mobilcom.debitel.cloud.android.ui.activity.action.DETAILS";
-
-    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.class.getSimpleName();
-
-    private static final String TAG_LIST_OF_FILES = "LIST_OF_FILES";
-    private static final String TAG_SECOND_FRAGMENT = "SECOND_FRAGMENT";
-
-    private OCFile mWaitingToPreview;
-    private Handler mHandler;
-    
-    private String mDownloadAddedMessage;
-    private String mDownloadFinishMessage;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        Log_OC.d(TAG, "onCreate() start");
-        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
-
-        super.onCreate(savedInstanceState); // this calls onAccountChanged() when ownCloud Account is valid
-
-        mHandler = new Handler();
-        
-        FileDownloader downloader = new FileDownloader();
-        mDownloadAddedMessage = downloader.getDownloadAddedMessage();
-        mDownloadFinishMessage= downloader.getDownloadFinishMessage();
-
-        /// bindings to transference services
-        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);
-
-        /// Load of saved instance state
-        if(savedInstanceState != null) {
-            mWaitingToPreview = (OCFile) savedInstanceState.getParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW);
-
-        } else {
-            mWaitingToPreview = null;
-        }
-
-        /// USER INTERFACE
-
-        // Inflate and set the layout view
-        setContentView(R.layout.files);    
-        mDualPane = getResources().getBoolean(R.bool.large_land_layout);
-        mLeftFragmentContainer = findViewById(R.id.left_fragment_container);
-        mRightFragmentContainer = findViewById(R.id.right_fragment_container);
-        if (savedInstanceState == null) {
-            createMinFragments();
-        }
-
-        // Action bar setup
-        mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
-        getSupportActionBar().setHomeButtonEnabled(true);       // mandatory since Android ICS, according to the official documentation
-        setSupportProgressBarIndeterminateVisibility(false);    // always AFTER setContentView(...) ; to work around bug in its implementation
-        
-        
-        
-        Log_OC.d(TAG, "onCreate() end");
-    }
-
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        if (mDownloadConnection != null)
-            unbindService(mDownloadConnection);
-        if (mUploadConnection != null)
-            unbindService(mUploadConnection);
-    }
-
-
-    /**
-     *  Called when the ownCloud {@link Account} associated to the Activity was just updated.
-     */ 
-    @Override
-    protected void onAccountSet(boolean stateWasRecovered) {
-        if (getAccount() != null) {
-            mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver());
-
-            /// Check whether the 'main' OCFile handled by the Activity is contained in the current Account
-            OCFile file = getFile();
-            // get parent from path
-            String parentPath = "";
-            if (file != null) {
-                if (file.isDown() && file.getLastSyncDateForProperties() == 0) {
-                    // upload in progress - right now, files are not inserted in the local cache until the upload is successful
-                    // get parent from path
-                    parentPath = file.getRemotePath().substring(0, file.getRemotePath().lastIndexOf(file.getFileName()));
-                    if (mStorageManager.getFileByPath(parentPath) ==  null)
-                        file = null; // not able to know the directory where the file is uploading
-                } else {
-                    file = mStorageManager.getFileByPath(file.getRemotePath());   // currentDir = null if not in the current Account
-                }
-            }
-            if (file == null) {
-                // fall back to root folder
-                file = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR);  // never returns null
-            }
-            setFile(file);
-            mDirectories.clear();
-            OCFile fileIt = file;
-            while(fileIt != null && fileIt.getFileName() != OCFile.PATH_SEPARATOR) {
-                if (fileIt.isDirectory()) {
-                    mDirectories.add(fileIt.getFileName());
-                }
-                // get parent from path
-                parentPath = fileIt.getRemotePath().substring(0, fileIt.getRemotePath().lastIndexOf(fileIt.getFileName()));
-                fileIt = mStorageManager.getFileByPath(parentPath);
-            }
-            mDirectories.add(OCFile.PATH_SEPARATOR);
-            if (!stateWasRecovered) {
-                Log_OC.e(TAG, "Initializing Fragments in onAccountChanged..");
-                initFragmentsWithFile();
-                
-            } else {
-                updateFragmentsVisibility(!file.isDirectory());
-                updateNavigationElementsInActionBar(file.isDirectory() ? null : file);
-            }
-            
-            
-        } else {
-            Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
-        }
-    }
-
-
-    private void createMinFragments() {
-        OCFileListFragment listOfFiles = new OCFileListFragment();
-        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
-        transaction.add(R.id.left_fragment_container, listOfFiles, TAG_LIST_OF_FILES);
-        transaction.commit();
-    }
-
-    private void initFragmentsWithFile() {
-        if (getAccount() != null && getFile() != null) {
-            /// First fragment
-            OCFileListFragment listOfFiles = getListOfFilesFragment(); 
-            if (listOfFiles != null) {
-                listOfFiles.listDirectory(getCurrentDir());   
-            } else {
-                Log.e(TAG, "Still have a chance to lose the initializacion of list fragment >(");
-            }
-            
-            /// Second fragment
-            OCFile file = getFile(); 
-            Fragment secondFragment = chooseInitialSecondFragment(file);
-            if (secondFragment != null) {
-                setSecondFragment(secondFragment);
-                updateFragmentsVisibility(true);
-                updateNavigationElementsInActionBar(file);
-                
-            } else {
-                cleanSecondFragment();
-            }
-
-        } else {
-            Log.wtf(TAG, "initFragments() called with invalid NULLs!");
-            if (getAccount() == null) {
-                Log.wtf(TAG, "\t account is NULL");
-            }
-            if (getFile() == null) {
-                Log.wtf(TAG, "\t file is NULL");
-            }
-        }
-    }
-
-    private Fragment chooseInitialSecondFragment(OCFile file) {
-        Fragment secondFragment = null;
-        if (file != null && !file.isDirectory()) {
-            if (file.isDown() && PreviewMediaFragment.canBePreviewed(file) 
-                    && file.getLastSyncDateForProperties() > 0  // temporal fix
-                    ) {
-                int startPlaybackPosition = getIntent().getIntExtra(PreviewVideoActivity.EXTRA_START_POSITION, 0);
-                boolean autoplay = getIntent().getBooleanExtra(PreviewVideoActivity.EXTRA_AUTOPLAY, true);
-                secondFragment = new PreviewMediaFragment(file, getAccount(), startPlaybackPosition, autoplay);
-
-            } else {
-                secondFragment = new FileDetailFragment(file, getAccount());
-            }
-        }
-        return secondFragment;
-    }
-
-
-    /**
-     * Replaces the second fragment managed by the activity with the received as
-     * a parameter.
-     * 
-     * Assumes never will be more than two fragments managed at the same time. 
-     * 
-     * @param fragment      New second Fragment to set.
-     */
-    private void setSecondFragment(Fragment fragment) {
-        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
-        transaction.replace(R.id.right_fragment_container, fragment, TAG_SECOND_FRAGMENT);
-        transaction.commit();
-    }
-
-
-    private void updateFragmentsVisibility(boolean existsSecondFragment) {
-        if (mDualPane) {
-            if (mLeftFragmentContainer.getVisibility() != View.VISIBLE) {
-                mLeftFragmentContainer.setVisibility(View.VISIBLE);
-            }
-            if (mRightFragmentContainer.getVisibility() != View.VISIBLE) {
-                mRightFragmentContainer.setVisibility(View.VISIBLE);
-            }
-
-        } else if (existsSecondFragment) {
-            if (mLeftFragmentContainer.getVisibility() != View.GONE) {
-                mLeftFragmentContainer.setVisibility(View.GONE);
-            }
-            if (mRightFragmentContainer.getVisibility() != View.VISIBLE) {
-                mRightFragmentContainer.setVisibility(View.VISIBLE);
-            }
-
-        } else {
-            if (mLeftFragmentContainer.getVisibility() != View.VISIBLE) {
-                mLeftFragmentContainer.setVisibility(View.VISIBLE);
-            }
-            if (mRightFragmentContainer.getVisibility() != View.GONE) {
-                mRightFragmentContainer.setVisibility(View.GONE);
-            }
-        }
-    }
-
-
-    private OCFileListFragment getListOfFilesFragment() {
-        Fragment listOfFiles = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES);
-        if (listOfFiles != null) {
-            return (OCFileListFragment)listOfFiles;
-        }
-        Log_OC.wtf(TAG, "Access to unexisting list of files fragment!!");
-        return null;
-    }
-
-    protected FileFragment getSecondFragment() {
-        Fragment second = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_SECOND_FRAGMENT);
-        if (second != null) {
-            return (FileFragment)second;
-        }
-        return null;
-    }
-
-    public void cleanSecondFragment() {
-        Fragment second = getSecondFragment();
-        if (second != null) {
-            FragmentTransaction tr = getSupportFragmentManager().beginTransaction();
-            tr.remove(second);
-            tr.commit();
-        }
-        updateFragmentsVisibility(false);
-        updateNavigationElementsInActionBar(null);
-    }
-
-    protected void refeshListOfFilesFragment() {
-        OCFileListFragment fileListFragment = getListOfFilesFragment();
-        if (fileListFragment != null) { 
-            fileListFragment.listDirectory();
-        }
-    }
-
-    protected void refreshSecondFragment(String downloadEvent, String downloadedRemotePath, boolean success) {
-        FileFragment secondFragment = getSecondFragment();
-        boolean waitedPreview = (mWaitingToPreview != null && mWaitingToPreview.getRemotePath().equals(downloadedRemotePath));
-        if (secondFragment != null && secondFragment instanceof FileDetailFragment) {
-            FileDetailFragment detailsFragment = (FileDetailFragment) secondFragment;
-            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(mDownloadAddedMessage)) {
-                // grant that the right panel updates the progress bar
-                detailsFragment.listenForTransferProgress();
-                detailsFragment.updateFileDetails(true, false);
-
-            } else if (downloadEvent.equals(mDownloadFinishMessage)) {
-                //  update the right panel
-                boolean detailsFragmentChanged = false;
-                if (waitedPreview) {
-                    if (success) {
-                        mWaitingToPreview = mStorageManager.getFileById(mWaitingToPreview.getFileId());   // update the file from database, for the local storage path
-                        if (PreviewMediaFragment.canBePreviewed(mWaitingToPreview)) {
-                            startMediaPreview(mWaitingToPreview, 0, true);
-                            detailsFragmentChanged = true;
-                        } else {
-                            openFile(mWaitingToPreview);
-                        }
-                    }
-                    mWaitingToPreview = null;
-                }
-                if (!detailsFragmentChanged) {
-                    detailsFragment.updateFileDetails(false, (success));
-                }
-            }
-        }
-    }
-
-
-    @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: {
-            FileFragment second = getSecondFragment();
-            OCFile currentDir = getCurrentDir();
-            if((currentDir != null && currentDir.getParentId() != 0) || 
-                    (second != null && second.getFile() != null)) {
-                onBackPressed(); 
-            }
-            break;
-        }
-        default:
-            retval = super.onOptionsItemSelected(item);
-        }
-        return retval;
-    }
-
-    private void startSynchronization() {
-        ContentResolver.cancelSync(null, MainApp.getAuthTokenType());   // cancel the current synchronizations of any ownCloud account
-        Bundle bundle = new Bundle();
-        bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
-        ContentResolver.requestSync(
-                getAccount(),
-                MainApp.getAuthTokenType(), 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
-     */
-    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-        super.onActivityResult(requestCode, resultCode, 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, getAccount());
-            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(TAG, "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(TAG, "Unexpected exception when trying to read the result of Intent.ACTION_GET_CONTENT", e);
-            e.printStackTrace();
-
-        } finally {
-            if (filepath == null) {
-                Log_OC.e(TAG, "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,
-                getAccount());
-        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() {
-        OCFileListFragment listOfFiles = getListOfFilesFragment(); 
-        if (mDualPane || getSecondFragment() == null) {
-            if (listOfFiles != null) {  // should never be null, indeed
-                if (mDirectories.getCount() <= 1) {
-                    finish();
-                    return;
-                }
-                popDirname();
-                listOfFiles.onBrowseUp();
-            }
-        }
-        if (listOfFiles != null) {  // should never be null, indeed
-            setFile(listOfFiles.getCurrentFile());
-        }
-        cleanSecondFragment();
-    }
-
-    @Override
-    protected void onSaveInstanceState(Bundle outState) {
-        // responsibility of restore is preferred in onCreate() before than in onRestoreInstanceState when there are Fragments involved
-        Log_OC.e(TAG, "onSaveInstanceState() start");
-        super.onSaveInstanceState(outState);
-        outState.putParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW, mWaitingToPreview);
-        Log_OC.d(TAG, "onSaveInstanceState() end");
-    }
-    
-
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        Log_OC.e(TAG, "onResume() start");
-
-        FileUploader fileUploader = new FileUploader();
-        FileSyncService fileSyncService = new FileSyncService();
-        
-        // Listen for sync messages
-        IntentFilter syncIntentFilter = new IntentFilter(fileSyncService.getSyncMessage());
-        mSyncBroadcastReceiver = new SyncBroadcastReceiver();
-        registerReceiver(mSyncBroadcastReceiver, syncIntentFilter);
-
-        // Listen for upload messages
-        IntentFilter uploadIntentFilter = new IntentFilter(fileUploader.getUploadFinishMessage());
-        mUploadFinishReceiver = new UploadFinishReceiver();
-        registerReceiver(mUploadFinishReceiver, uploadIntentFilter);
-
-        // Listen for download messages
-        IntentFilter downloadIntentFilter = new IntentFilter(mDownloadAddedMessage);
-        downloadIntentFilter.addAction(mDownloadFinishMessage);
-        mDownloadFinishReceiver = new DownloadFinishReceiver();
-        registerReceiver(mDownloadFinishReceiver, downloadIntentFilter);
-    
-        Log_OC.d(TAG, "onResume() end");
-    }
-
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        Log_OC.e(TAG, "onPause() start");
-        if (mSyncBroadcastReceiver != null) {
-            unregisterReceiver(mSyncBroadcastReceiver);
-            mSyncBroadcastReceiver = null;
-        }
-        if (mUploadFinishReceiver != null) {
-            unregisterReceiver(mUploadFinishReceiver);
-            mUploadFinishReceiver = null;
-        }
-        if (mDownloadFinishReceiver != null) {
-            unregisterReceiver(mDownloadFinishReceiver);
-            mDownloadFinishReceiver = null;
-        }
-
-        Log_OC.d(TAG, "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_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, FileDisplayActivity.this.getAccount());
-                            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) {
-                        Intent action = new Intent(FileDisplayActivity.this, InstantUploadActivity.class);
-                        action.putExtra(FileUploader.KEY_ACCOUNT, FileDisplayActivity.this.getAccount());
-                        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;
-    }
-
-
-    /**
-     * Show loading dialog 
-     */
-    public void showLoadingDialog() {
-        // Construct dialog
-        LoadingDialog loading = new LoadingDialog(getResources().getString(R.string.wait_a_moment));
-        FragmentManager fm = getSupportFragmentManager();
-        FragmentTransaction ft = fm.beginTransaction();
-        loading.show(ft, DIALOG_WAIT_TAG);
-        
-    }
-    
-    /**
-     * Dismiss loading dialog
-     */
-    public void dismissLoadingDialog(){
-        Fragment frag = getSupportFragmentManager().findFragmentByTag(DIALOG_WAIT_TAG);
-      if (frag != null) {
-          LoadingDialog loading = (LoadingDialog) frag;
-            loading.dismiss();
-        }
-    }
-    
-    
-    /**
-     * 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);
-        setFile(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(TAG, "sync of account " + accountName + " is in_progress: " + inProgress);
-
-            if (getAccount() != null && accountName.equals(getAccount().name)
-                    && mStorageManager != null
-                    ) {  
-
-                String synchFolderRemotePath = intent.getStringExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH); 
-
-                boolean fillBlankRoot = false;
-                OCFile currentDir = getCurrentDir();
-                if (currentDir == null) {
-                    currentDir = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR);
-                    fillBlankRoot = (currentDir != null);                   
-                }
-
-                if ((synchFolderRemotePath != null && currentDir != null && (currentDir.getRemotePath().equals(synchFolderRemotePath)))
-                        || fillBlankRoot ) {
-                    if (!fillBlankRoot) 
-                        currentDir = mStorageManager.getFileByPath(synchFolderRemotePath);
-                    OCFileListFragment fileListFragment = getListOfFilesFragment();
-                    if (fileListFragment != null) {
-                        fileListFragment.listDirectory(currentDir);
-                    }
-                    if (getSecondFragment() == null)
-                        setFile(currentDir);
-                }
-                
-                setSupportProgressBarIndeterminateVisibility(inProgress);
-                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 = getAccount() != null && accountName.equals(getAccount().name);
-            OCFile currentDir = getCurrentDir();
-            boolean isDescendant = (currentDir != null) && (uploadedRemotePath != null) && (uploadedRemotePath.startsWith(currentDir.getRemotePath()));
-            if (sameAccount && isDescendant) {
-                refeshListOfFilesFragment();
-            }
-        }
-
-    }
-
-
-    /**
-     * 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) {
-                refeshListOfFilesFragment();
-                refreshSecondFragment(intent.getAction(), downloadedRemotePath, intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false));
-            }
-
-            removeStickyBroadcast(intent);
-        }
-
-        private boolean isDescendant(String downloadedRemotePath) {
-            OCFile currentDir = getCurrentDir();
-            return (currentDir != null && downloadedRemotePath != null && downloadedRemotePath.startsWith(currentDir.getRemotePath()));
-        }
-
-        private boolean isSameAccount(Context context, Intent intent) {
-            String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME);
-            return (accountName != null && getAccount() != null && accountName.equals(getAccount().name));
-        }
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public DataStorageManager getStorageManager() {
-        return mStorageManager;
-    }
-
-
-    /**
-     * {@inheritDoc}
-     * 
-     * Updates action bar and second fragment, if in dual pane mode.
-     */
-    @Override
-    public void onBrowsedDownTo(OCFile directory) {
-        pushDirname(directory);
-        cleanSecondFragment();
-    }
-
-    /**
-     * Opens the image gallery showing the image {@link OCFile} received as parameter.
-     * 
-     * @param file                      Image {@link OCFile} to show.
-     */
-    @Override
-    public void startImagePreview(OCFile file) {
-        Intent showDetailsIntent = new Intent(this, PreviewImageActivity.class);
-        showDetailsIntent.putExtra(EXTRA_FILE, file);
-        showDetailsIntent.putExtra(EXTRA_ACCOUNT, getAccount());
-        startActivity(showDetailsIntent);
-    }
-
-    /**
-     * Stars the preview of an already down media {@link OCFile}.
-     * 
-     * @param file                      Media {@link OCFile} to preview.
-     * @param startPlaybackPosition     Media position where the playback will be started, in milliseconds.
-     * @param autoplay                  When 'true', the playback will start without user interactions.
-     */
-    @Override
-    public void startMediaPreview(OCFile file, int startPlaybackPosition, boolean autoplay) {
-        Fragment mediaFragment = new PreviewMediaFragment(file, getAccount(), startPlaybackPosition, autoplay);
-        setSecondFragment(mediaFragment);
-        updateFragmentsVisibility(true);
-        updateNavigationElementsInActionBar(file);
-        setFile(file);
-    }
-
-    /**
-     * Requests the download of the received {@link OCFile} , updates the UI
-     * to monitor the download progress and prepares the activity to preview
-     * or open the file when the download finishes.
-     * 
-     * @param file          {@link OCFile} to download and preview.
-     */
-    @Override
-    public void startDownloadForPreview(OCFile file) {
-        Fragment detailFragment = new FileDetailFragment(file, getAccount());
-        setSecondFragment(detailFragment);
-        mWaitingToPreview = file;
-        requestForDownload();
-        updateFragmentsVisibility(true);
-        updateNavigationElementsInActionBar(file);
-        setFile(file);
-    }
-
-
-    /**
-     * Shows the information of the {@link OCFile} received as a 
-     * parameter in the second fragment.
-     * 
-     * @param file          {@link OCFile} whose details will be shown
-     */
-    @Override
-    public void showDetails(OCFile file) {
-        Fragment detailFragment = new FileDetailFragment(file, getAccount());
-        setSecondFragment(detailFragment);
-        updateFragmentsVisibility(true);
-        updateNavigationElementsInActionBar(file);
-        setFile(file);
-    }
-
-
-    /**
-     * TODO
-     */
-    private void updateNavigationElementsInActionBar(OCFile chosenFile) {
-        ActionBar actionBar = getSupportActionBar(); 
-        if (chosenFile == null || mDualPane) {
-            // only list of files - set for browsing through folders
-            OCFile currentDir = getCurrentDir();
-            actionBar.setDisplayHomeAsUpEnabled(currentDir != null && currentDir.getParentId() != 0);
-            actionBar.setDisplayShowTitleEnabled(false);
-            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
-            actionBar.setListNavigationCallbacks(mDirectories, this);   // assuming mDirectories is updated
-
-        } else {
-            actionBar.setDisplayHomeAsUpEnabled(true);
-            actionBar.setDisplayShowTitleEnabled(true);
-            actionBar.setTitle(chosenFile.getFileName());
-            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
-        }
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onFileStateChanged() {
-        refeshListOfFilesFragment();
-        updateNavigationElementsInActionBar(getSecondFragment().getFile());
-    }
-
-
-    /**
-     * {@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
-            OCFileListFragment listOfFiles = getListOfFilesFragment(); 
-            if (listOfFiles != null) {
-                listOfFiles.listDirectory();
-            }
-            FileFragment secondFragment = getSecondFragment();
-            if (secondFragment != null && secondFragment instanceof FileDetailFragment) {
-                FileDetailFragment detailFragment = (FileDetailFragment)secondFragment;
-                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) {
-        dismissLoadingDialog();
-        if (result.isSuccess()) {
-            Toast msg = Toast.makeText(this, R.string.remove_success_msg, Toast.LENGTH_LONG);
-            msg.show();
-            OCFile removedFile = operation.getFile();
-            getSecondFragment();
-            FileFragment second = getSecondFragment();
-            if (second != null && removedFile.equals(second.getFile())) {
-                cleanSecondFragment();
-            }
-            if (mStorageManager.getFileById(removedFile.getParentId()).equals(getCurrentDir())) {
-                refeshListOfFilesFragment();
-            }
-
-        } 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()) {
-            dismissLoadingDialog();
-            refeshListOfFilesFragment();
-
-        } else {
-            //dismissDialog(DIALOG_SHORT_WAIT);
-            dismissLoadingDialog();
-            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) {
-        dismissLoadingDialog();
-        OCFile renamedFile = operation.getFile();
-        if (result.isSuccess()) {
-            if (mDualPane) {
-                FileFragment details = getSecondFragment();
-                if (details != null && details instanceof FileDetailFragment && renamedFile.equals(details.getFile()) ) {
-                    ((FileDetailFragment) details).updateFileDetails(renamedFile, getAccount());
-                }
-            }
-            if (mStorageManager.getFileById(renamedFile.getParentId()).equals(getCurrentDir())) {
-                refeshListOfFilesFragment();
-            }
-
-        } 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) {
-        dismissLoadingDialog();
-        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, getAccount());
-                startActivity(i);
-
-            } else {
-                Toast msg = Toast.makeText(this, R.string.sync_file_fail_msg, Toast.LENGTH_LONG); 
-                msg.show();
-            }
-
-        } else {
-            if (operation.transferWasRequested()) {
-                refeshListOfFilesFragment();
-                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) {
-        if (mDualPane) {
-            FileFragment details = getSecondFragment();
-            if (details != null && details instanceof FileDetailFragment && file.equals(details.getFile()) ) {
-                if (downloading || uploading) {
-                    ((FileDetailFragment)details).updateFileDetails(file, getAccount());
-                } else {
-                    ((FileDetailFragment)details).updateFileDetails(false, true);
-                }
-            }
-        }
-    }
-
-
-    public void onDismiss(EditNameDialog dialog) {
-        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 = getCurrentDir().getRemotePath();
-
-                // Create directory
-                path += newDirectoryName + OCFile.PATH_SEPARATOR;
-                RemoteOperation operation = new CreateFolderOperation(path, false, mStorageManager);
-                operation.execute(  getAccount(), 
-                        FileDisplayActivity.this, 
-                        FileDisplayActivity.this, 
-                        mHandler,
-                        FileDisplayActivity.this);
-
-                showLoadingDialog();
-            }
-        }
-    }
-
-
-    private void requestForDownload() {
-        Account account = getAccount();
-        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);
-        }
-    }
-
-
-    private OCFile getCurrentDir() {
-        OCFile file = getFile();
-        if (file != null) {
-            if (file.isDirectory()) {
-                return file;
-            } else if (mStorageManager != null) {
-                String parentPath = file.getRemotePath().substring(0, file.getRemotePath().lastIndexOf(file.getFileName()));
-                return mStorageManager.getFileByPath(parentPath);
-            }
-        }
-        return null;
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/activity/GenericExplanationActivity.java b/src/de/mobilcom/debitel/cloud/android/ui/activity/GenericExplanationActivity.java
deleted file mode 100644 (file)
index 2921a83..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.activity;
-
-import java.util.ArrayList;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.text.method.ScrollingMovementMethod;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-
-import de.mobilcom.debitel.cloud.android.R;
-
-/**
- * Activity showing a text message and, optionally, a couple list of single or paired text strings.
- * 
- * Added to show explanations for notifications when the user clicks on them, and there no place
- * better to show them.
- * 
- * @author David A. Velasco
- */
-public class GenericExplanationActivity  extends SherlockFragmentActivity {
-
-    public static final String EXTRA_LIST = GenericExplanationActivity.class.getCanonicalName() + ".EXTRA_LIST";
-    public static final String EXTRA_LIST_2 = GenericExplanationActivity.class.getCanonicalName() + ".EXTRA_LIST_2";
-    public static final String MESSAGE = GenericExplanationActivity.class.getCanonicalName() + ".MESSAGE";
-    
-    
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        
-        Intent intent = getIntent();
-        String message = intent.getStringExtra(MESSAGE); 
-        ArrayList<String> list = intent.getStringArrayListExtra(EXTRA_LIST);
-        ArrayList<String> list2 = intent.getStringArrayListExtra(EXTRA_LIST_2);
-        
-        setContentView(R.layout.generic_explanation);
-        
-        if (message != null) {
-            TextView textView = (TextView) findViewById(R.id.message);
-            textView.setText(message);
-            textView.setMovementMethod(new ScrollingMovementMethod());
-        }
-        
-        ListView listView = (ListView) findViewById(R.id.list);
-        if (list != null && list.size() > 0) {
-            //ListAdapter adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list);
-            ListAdapter adapter = new ExplanationListAdapterView(this, list, list2);
-            listView.setAdapter(adapter);
-        } else {
-            listView.setVisibility(View.GONE);
-        }
-    }
-    
-    public class ExplanationListAdapterView extends ArrayAdapter<String> {
-        
-        ArrayList<String> mList;
-        ArrayList<String> mList2;
-        
-        ExplanationListAdapterView(Context context, ArrayList<String> list, ArrayList<String> list2) {
-            super(context, android.R.layout.two_line_list_item, android.R.id.text1, list);
-            mList = list;
-            mList2 = list2;
-        }
-
-        @Override
-        public boolean isEnabled(int position) {
-            return false;
-        }
-        
-        /**
-         * {@inheritDoc}
-         */
-        @Override
-        public View getView (int position, View convertView, ViewGroup parent) {
-            View view = super.getView(position, convertView, parent);
-            if (view != null)  {
-                if (mList2 != null && mList2.size() > 0 && position >= 0 && position < mList2.size()) {
-                    TextView text2 = (TextView) view.findViewById(android.R.id.text2);
-                    if (text2 != null) {
-                        text2.setText(mList2.get(position));
-                    }
-                }
-            }
-            return view;
-        }
-    }
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/activity/InstantUploadActivity.java b/src/de/mobilcom/debitel/cloud/android/ui/activity/InstantUploadActivity.java
deleted file mode 100644 (file)
index 23cb6f0..0000000
+++ /dev/null
@@ -1,476 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.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 de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-import de.mobilcom.debitel.cloud.android.db.DbHandler;
-import de.mobilcom.debitel.cloud.android.files.InstantUploadBroadcastReceiver;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader;
-import de.mobilcom.debitel.cloud.android.ui.CustomButton;
-import de.mobilcom.debitel.cloud.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);
-
-        CustomButton deleteAllBtn = (CustomButton) findViewById(R.id.failed_upload_delete_all_btn);
-        deleteAllBtn.setOnClickListener(getDeleteListner());
-        CustomButton retryAllBtn = (CustomButton) findViewById(R.id.failed_upload_retry_all_btn);
-        retryAllBtn.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.background_color);
-                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.background_color);
-        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.background_color);
-        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.background_color);
-        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(de.mobilcom.debitel.cloud.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
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/activity/LandingActivity.java b/src/de/mobilcom/debitel/cloud/android/ui/activity/LandingActivity.java
deleted file mode 100644 (file)
index 5d6b69f..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-/* ownCloud Android client application\r
- *   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 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
-package de.mobilcom.debitel.cloud.android.ui.activity;\r
-\r
-import com.actionbarsherlock.app.SherlockFragmentActivity;\r
-\r
-import android.accounts.Account;\r
-import android.accounts.AccountManager;\r
-import android.app.AlertDialog;\r
-import android.app.Dialog;\r
-import android.content.DialogInterface;\r
-import android.content.DialogInterface.OnClickListener;\r
-import android.content.Intent;\r
-import android.os.Bundle;\r
-import android.view.View;\r
-import android.widget.AdapterView;\r
-import android.widget.AdapterView.OnItemClickListener;\r
-import android.widget.GridView;\r
-import android.widget.Toast;\r
-\r
-import de.mobilcom.debitel.cloud.android.MainApp;\r
-import de.mobilcom.debitel.cloud.android.R;\r
-import de.mobilcom.debitel.cloud.android.ui.adapter.LandingScreenAdapter;\r
-\r
-/**\r
- * This activity is used as a landing page when the user first opens this app.\r
- * \r
- * @author Lennart Rosam\r
- * \r
- */\r
-public class LandingActivity extends SherlockFragmentActivity implements\r
-        OnClickListener, OnItemClickListener {\r
-\r
-    public static final int DIALOG_SETUP_ACCOUNT = 1;\r
-\r
-    @Override\r
-    protected void onCreate(Bundle savedInstanceState) {\r
-        super.onCreate(savedInstanceState);\r
-        setContentView(R.layout.main);\r
-\r
-        // Fill the grid view of the landing screen with icons\r
-        GridView landingScreenItems = (GridView) findViewById(R.id.homeScreenGrid);\r
-        landingScreenItems.setAdapter(new LandingScreenAdapter(this));\r
-        landingScreenItems.setOnItemClickListener(this);\r
-\r
-        // Check, if there are ownCloud accounts\r
-        if (!accountsAreSetup()) {\r
-            showDialog(DIALOG_SETUP_ACCOUNT);\r
-        } else {\r
-            // Start device tracking service\r
-            Intent locationServiceIntent = new Intent();\r
-            locationServiceIntent\r
-                    .setAction("de.mobilcom.debitel.cloud.android.location.LocationLauncher");\r
-            sendBroadcast(locationServiceIntent);\r
-        }\r
-\r
-    }\r
-\r
-    @Override\r
-    protected void onRestart() {\r
-        super.onRestart();\r
-        // Check, if there are ownCloud accounts\r
-        if (!accountsAreSetup()) {\r
-            showDialog(DIALOG_SETUP_ACCOUNT);\r
-        }\r
-    }\r
-\r
-    @Override\r
-    protected void onRestoreInstanceState(Bundle savedInstanceState) {\r
-        super.onRestoreInstanceState(savedInstanceState);\r
-        // Check, if there are ownCloud accounts\r
-        if (!accountsAreSetup()) {\r
-            showDialog(DIALOG_SETUP_ACCOUNT);\r
-        }\r
-    }\r
-\r
-    @Override\r
-    protected Dialog onCreateDialog(int id) {\r
-        Dialog dialog;\r
-        switch (id) {\r
-        case DIALOG_SETUP_ACCOUNT:\r
-            AlertDialog.Builder 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(R.string.common_ok, this);\r
-            builder.setNegativeButton(R.string.common_cancel, this);\r
-            dialog = builder.create();\r
-            break;\r
-        default:\r
-            dialog = null;\r
-        }\r
-\r
-        return dialog;\r
-    }\r
-\r
-    public void onClick(DialogInterface dialog, int which) {\r
-        // In any case - we won't need it anymore\r
-        dialog.dismiss();\r
-        switch (which) {\r
-        case DialogInterface.BUTTON_POSITIVE:\r
-            Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT);\r
-            intent.putExtra("authorities",\r
-                    new String[] { MainApp.getAuthTokenType() });\r
-            startActivity(intent);\r
-            break;\r
-        case DialogInterface.BUTTON_NEGATIVE:\r
-            finish();\r
-        }\r
-\r
-    }\r
-\r
-    @Override\r
-    /**\r
-     * Start an activity based on the selection\r
-     * the user made\r
-     */\r
-    public void onItemClick(AdapterView<?> parent, View view, int position,\r
-            long id) {\r
-        Intent intent;\r
-        intent = (Intent) parent.getAdapter().getItem(position);\r
-        if (intent != null) {\r
-            startActivity(intent);\r
-        } else {\r
-            // TODO: Implement all of this and make this text go away ;-)\r
-            Toast toast = Toast.makeText(this, "Not yet implemented!",\r
-                    Toast.LENGTH_SHORT);\r
-            toast.show();\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Checks, whether or not there are any ownCloud accounts setup.\r
-     * \r
-     * @return true, if there is at least one account.\r
-     */\r
-    private boolean accountsAreSetup() {\r
-        AccountManager accMan = AccountManager.get(this);\r
-        Account[] accounts = accMan\r
-                .getAccountsByType(MainApp.getAccountType());\r
-        return accounts.length > 0;\r
-    }\r
-\r
-}\r
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/activity/LogHistoryActivity.java b/src/de/mobilcom/debitel/cloud/android/ui/activity/LogHistoryActivity.java
deleted file mode 100644 (file)
index 20317bc..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.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.ListView;
-
-import com.actionbarsherlock.app.ActionBar;
-import com.actionbarsherlock.app.SherlockPreferenceActivity;
-import com.actionbarsherlock.view.MenuItem;
-
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.ui.CustomButton;
-import de.mobilcom.debitel.cloud.android.ui.adapter.LogListAdapter;
-import de.mobilcom.debitel.cloud.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);
-        CustomButton deleteHistoryButton = (CustomButton) 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
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/activity/PinCodeActivity.java b/src/de/mobilcom/debitel/cloud/android/ui/activity/PinCodeActivity.java
deleted file mode 100644 (file)
index 37e763d..0000000
+++ /dev/null
@@ -1,638 +0,0 @@
-/* ownCloud Android client application
- *   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 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 de.mobilcom.debitel.cloud.android.ui.activity;
-
-import java.util.Arrays;
-
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.ui.CustomButton;
-
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.Bundle;
-import android.preference.PreferenceManager;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnFocusChangeListener;
-import android.view.View.OnKeyListener;
-import android.widget.EditText;
-import android.widget.TextView;
-
-public class PinCodeActivity extends SherlockFragmentActivity {
-
-  
-    public final static String EXTRA_ACTIVITY = "de.mobilcom.debitel.cloud.android.ui.activity.PinCodeActivity.ACTIVITY";
-    public final static String EXTRA_NEW_STATE = "de.mobilcom.debitel.cloud.android.ui.activity.PinCodeActivity.NEW_STATE";
-    
-    CustomButton bCancel;
-    TextView mPinHdr;
-    TextView mPinHdrExplanation;
-    EditText mText1;
-    EditText mText2;
-    EditText mText3;
-    EditText mText4;
-    
-    String [] tempText ={"","","",""};
-    
-    String activity;
-    
-    boolean confirmingPinCode = false;
-    boolean pinCodeChecked = false;
-    boolean newPasswordEntered = false;
-    boolean bChange = true; // to control that only one blocks jump
-    int tCounter ; // Count the number of attempts an user could introduce the PIN code
-
-    
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.pincodelock); 
-        
-        Intent intent = getIntent();
-        activity = intent.getStringExtra(EXTRA_ACTIVITY);
-     
-        bCancel = (CustomButton) findViewById(R.id.cancel);
-        mPinHdr = (TextView) findViewById(R.id.pinHdr);
-        mPinHdrExplanation = (TextView) findViewById(R.id.pinHdrExpl);
-        mText1 = (EditText) findViewById(R.id.txt1);
-        mText1.requestFocus();
-        getWindow().setSoftInputMode(android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
-        mText2 = (EditText) findViewById(R.id.txt2);
-        mText3 = (EditText) findViewById(R.id.txt3);
-        mText4 = (EditText) findViewById(R.id.txt4);
-        
-        SharedPreferences appPrefs = PreferenceManager
-                .getDefaultSharedPreferences(getApplicationContext());
-        
-        // Not PIN Code defined yet.
-        // In a previous version settings is allow from start
-        if ( (appPrefs.getString("PrefPinCode1", null) == null ) ){
-            setChangePincodeView(true);
-            pinCodeChecked = true; 
-            newPasswordEntered = true;
-            
-        }else{ 
-            
-            if (appPrefs.getBoolean("set_pincode", false)){
-               // pincode activated
-               if (activity.equals("preferences")){
-                // PIN has been activated yet
-                 mPinHdr.setText(R.string.pincode_configure_your_pin);
-                 mPinHdrExplanation.setVisibility(View.VISIBLE);
-                 pinCodeChecked = true ; // No need to check it 
-                 setChangePincodeView(true);
-               }else{
-                // PIN active
-                 bCancel.setVisibility(View.INVISIBLE);
-                 bCancel.setVisibility(View.GONE);
-                 mPinHdr.setText(R.string.pincode_enter_pin_code);
-                 mPinHdrExplanation.setVisibility(View.INVISIBLE);
-                 setChangePincodeView(false);
-              }
-            
-           }else {
-            // pincode removal
-              mPinHdr.setText(R.string.pincode_remove_your_pincode);
-              mPinHdrExplanation.setVisibility(View.INVISIBLE);
-              pinCodeChecked = false;
-              setChangePincodeView(true); 
-           }
-           
-        }
-        setTextListeners();
-        
-        
-    }
-    
-
-     
-    protected void setInitVars(){
-        confirmingPinCode = false;
-        pinCodeChecked = false;
-        newPasswordEntered = false;
-
-    }
-    
-    protected void setInitView(){
-        bCancel.setVisibility(View.INVISIBLE);
-        bCancel.setVisibility(View.GONE);
-        mPinHdr.setText(R.string.pincode_enter_pin_code);
-        mPinHdrExplanation.setVisibility(View.INVISIBLE);
-    }
-    
-   
-    protected void setChangePincodeView(boolean state){
-       
-        if(state){
-        bCancel.setVisibility(View.VISIBLE);
-        bCancel.setOnClickListener(new OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            
-            SharedPreferences.Editor appPrefsE = PreferenceManager
-                    .getDefaultSharedPreferences(getApplicationContext()).edit();
-            
-            SharedPreferences appPrefs = PreferenceManager
-                    .getDefaultSharedPreferences(getApplicationContext());
-            
-            boolean state = appPrefs.getBoolean("set_pincode", false);
-            appPrefsE.putBoolean("set_pincode",!state); 
-            appPrefsE.commit();
-            setInitVars();
-            finish();
-            }
-        });
-        }  
-    
-    }
-    
-    
-    
-    /*
-     *  
-     */
-    protected void setTextListeners(){
-    
-        /*------------------------------------------------
-         *  FIRST BOX
-         -------------------------------------------------*/
-        
-        mText1.addTextChangedListener(new TextWatcher() {
-
-            @Override
-            public void onTextChanged(CharSequence s, int start, int before,
-                    int count) {
-            }
-
-            @Override
-            public void beforeTextChanged(CharSequence s, int start, int count,
-                    int after) {
-            }
-
-            @Override
-            public void afterTextChanged(Editable s) {
-                if (s.length() > 0) {
-                    if (!confirmingPinCode){
-                       tempText[0] = mText1.getText().toString();
-                       
-                    }
-                    mText2.requestFocus();
-                 }
-            }
-        });
-        
-        
-
-        /*------------------------------------------------
-         *  SECOND BOX 
-         -------------------------------------------------*/
-        mText2.addTextChangedListener(new TextWatcher() {
-
-            @Override
-            public void onTextChanged(CharSequence s, int start, int before,
-                    int count) {
-            }
-
-            @Override
-            public void beforeTextChanged(CharSequence s, int start, int count,
-                    int after) {
-            }
-
-            @Override
-            public void afterTextChanged(Editable s) {
-                if (s.length() > 0) {
-                    if (!confirmingPinCode){
-                        tempText[1] = mText2.getText().toString();
-                    }
-                    
-                    mText3.requestFocus();
-                }
-            }
-        });
-        mText2.setOnKeyListener(new OnKeyListener() {
-
-            @Override
-            public boolean onKey(View v, int keyCode, KeyEvent event) {
-                if (keyCode == KeyEvent.KEYCODE_DEL && bChange) {
-
-                    mText1.setText("");
-                    mText1.requestFocus();
-                    if (!confirmingPinCode)
-                       tempText[0] = "";
-                    bChange= false;
-                
-                }else if(!bChange){
-                    bChange=true;
-                    
-                }
-                return false;
-            }
-        });        
-        mText2.setOnFocusChangeListener(new OnFocusChangeListener() {
-               
-            @Override
-            public void onFocusChange(View v, boolean hasFocus) {
-                mText2.setCursorVisible(true);
-                if (mText1.getText().toString().equals("")){
-                    mText2.setSelected(false);
-                    mText2.setCursorVisible(false);
-                    mText1.requestFocus(); 
-                    mText1.setSelected(true);
-                    mText1.setSelection(0);
-                }
-                
-            }
-        });
-        
-        
-        /*------------------------------------------------
-         *  THIRD BOX
-         -------------------------------------------------*/
-        mText3.addTextChangedListener(new TextWatcher() {
-
-            @Override
-            public void onTextChanged(CharSequence s, int start, int before,
-                    int count) {
-            }
-
-            @Override
-            public void beforeTextChanged(CharSequence s, int start, int count,
-                    int after) {
-            }
-
-            @Override
-            public void afterTextChanged(Editable s) {
-                if (s.length() > 0) {
-                    if (!confirmingPinCode){
-                        tempText[2] = mText3.getText().toString();
-                    }
-                    mText4.requestFocus();
-                }
-            }
-        });
-        
-        mText3.setOnKeyListener(new OnKeyListener() {
-
-            @Override
-            public boolean onKey(View v, int keyCode, KeyEvent event) {
-                if (keyCode == KeyEvent.KEYCODE_DEL && bChange) {
-                    mText2.requestFocus();
-                    if (!confirmingPinCode)
-                        tempText[1] = "";
-                    mText2.setText("");
-                    bChange= false;
-                    
-                }else if(!bChange){
-                    bChange=true;                        
-                    
-                }
-                return false;
-            }
-        });
-        
-        mText3.setOnFocusChangeListener(new OnFocusChangeListener() {
-            
-            @Override
-            public void onFocusChange(View v, boolean hasFocus) {
-                mText3.setCursorVisible(true);
-                if (mText1.getText().toString().equals("")){
-                    mText3.setSelected(false);
-                    mText3.setCursorVisible(false);
-                    mText1.requestFocus();
-                    mText1.setSelected(true);
-                    mText1.setSelection(0);
-                }else if (mText2.getText().toString().equals("")){
-                    mText3.setSelected(false);
-                    mText3.setCursorVisible(false);
-                    mText2.requestFocus();
-                    mText2.setSelected(true);
-                    mText2.setSelection(0);
-                }
-                
-            }
-        });
-        
-        /*------------------------------------------------
-         *  FOURTH BOX
-         -------------------------------------------------*/
-        mText4.addTextChangedListener(new TextWatcher() {
-
-            @Override
-            public void onTextChanged(CharSequence s, int start, int before,
-                    int count) {
-            }
-
-            @Override
-            public void beforeTextChanged(CharSequence s, int start, int count,
-                    int after) {
-            }
-
-            @Override
-            public void afterTextChanged(Editable s) {
-                if (s.length() > 0) {
-                    
-                    if (!confirmingPinCode){
-                       tempText[3] = mText4.getText().toString();
-                    }
-                    mText1.requestFocus();
-
-                    if (!pinCodeChecked){
-                        pinCodeChecked = checkPincode();
-                    }
-                    
-                    if (pinCodeChecked && activity.equals("FileDisplayActivity")){
-                        finish();
-                    } else if (pinCodeChecked){
-                        
-                        Intent intent = getIntent();
-                        String newState = intent.getStringExtra(EXTRA_NEW_STATE);
-                        
-                        if (newState.equals("false")){
-                            SharedPreferences.Editor appPrefs = PreferenceManager
-                                    .getDefaultSharedPreferences(getApplicationContext()).edit();
-                            appPrefs.putBoolean("set_pincode",false);
-                            appPrefs.commit();
-                            
-                            setInitVars();
-                            pinCodeEnd(false);
-                            
-                        }else{
-                        
-                            if (!confirmingPinCode){
-                                pinCodeChangeRequest();
-                             
-                            } else {
-                                confirmPincode();
-                            }
-                        }
-                   
-                        
-                    }    
-                }
-            }
-        });
-
-        
-        
-        mText4.setOnKeyListener(new OnKeyListener() {
-
-            @Override
-            public boolean onKey(View v, int keyCode, KeyEvent event) {
-                if (keyCode == KeyEvent.KEYCODE_DEL && bChange) {
-                    mText3.requestFocus();
-                    if (!confirmingPinCode)
-                        tempText[2]="";
-                    mText3.setText("");
-                    bChange= false;
-                    
-                }else if(!bChange){
-                    bChange=true;    
-                }
-                return false;
-            }
-        });
-        
-       mText4.setOnFocusChangeListener(new OnFocusChangeListener() {
-            
-            @Override
-            public void onFocusChange(View v, boolean hasFocus) {
-                mText4.setCursorVisible(true);
-                
-                if (mText1.getText().toString().equals("")){
-                    mText4.setSelected(false);
-                    mText4.setCursorVisible(false);
-                    mText1.requestFocus();
-                    mText1.setSelected(true);
-                    mText1.setSelection(0);
-                }else if (mText2.getText().toString().equals("")){
-                    mText4.setSelected(false);
-                    mText4.setCursorVisible(false);
-                    mText2.requestFocus();
-                    mText2.setSelected(true);
-                    mText2.setSelection(0);
-                }else if (mText3.getText().toString().equals("")){
-                    mText4.setSelected(false);
-                    mText4.setCursorVisible(false);
-                    mText3.requestFocus();
-                    mText3.setSelected(true);
-                    mText3.setSelection(0);
-                }
-                
-            }
-        });
-        
-        
-        
-    } // end setTextListener
-    
-    
-    protected void pinCodeChangeRequest(){
-    
-        clearBoxes(); 
-        mPinHdr.setText(R.string.pincode_reenter_your_pincode); 
-        mPinHdrExplanation.setVisibility(View.INVISIBLE);        
-        confirmingPinCode =true;
-        
-    }
-    
-    
-    protected boolean checkPincode(){
-        
-        
-        SharedPreferences appPrefs = PreferenceManager
-                .getDefaultSharedPreferences(getApplicationContext());
-        
-       String pText1 = appPrefs.getString("PrefPinCode1", null);
-        String pText2 = appPrefs.getString("PrefPinCode2", null);
-        String pText3 = appPrefs.getString("PrefPinCode3", null);
-        String pText4 = appPrefs.getString("PrefPinCode4", null);
-
-        if ( tempText[0].equals(pText1) && 
-             tempText[1].equals(pText2) &&
-             tempText[2].equals(pText3) &&
-             tempText[3].equals(pText4) ) {
-            
-            return true;
-        
-        
-        }else {
-            Arrays.fill(tempText, null);
-            AlertDialog aDialog = new AlertDialog.Builder(this).create();
-            CharSequence errorSeq = getString(R.string.common_error);
-            aDialog.setTitle(errorSeq);
-            CharSequence cseq = getString(R.string.pincode_wrong);
-            aDialog.setMessage(cseq);
-            CharSequence okSeq = getString(R.string.common_ok);
-            aDialog.setButton(okSeq, new DialogInterface.OnClickListener(){
-
-                @Override
-                public void onClick(DialogInterface dialog, int which) {
-                   return; 
-                }
-                
-            });
-            aDialog.show();
-            clearBoxes(); 
-            mPinHdr.setText(R.string.pincode_enter_pin_code);
-            mPinHdrExplanation.setVisibility(View.INVISIBLE);
-            newPasswordEntered = true;
-            confirmingPinCode = false;
-            
-        }
-     
-        
-        return false;
-    }
-    
-    protected void confirmPincode(){
-        
-        confirmingPinCode = false;
-        
-        String rText1 = mText1.getText().toString();
-        String rText2 = mText2.getText().toString();
-        String rText3 = mText3.getText().toString();
-        String rText4 = mText4.getText().toString();
-        
-        if ( tempText[0].equals(rText1) && 
-             tempText[1].equals(rText2) &&
-             tempText[2].equals(rText3) &&
-             tempText[3].equals(rText4) ) {
-                        
-            savePincodeAndExit();
-            
-        } else {
-            
-            Arrays.fill(tempText, null);
-            AlertDialog aDialog = new AlertDialog.Builder(this).create();
-            CharSequence errorSeq = getString(R.string.common_error);
-            aDialog.setTitle(errorSeq);
-            CharSequence cseq = getString(R.string.pincode_mismatch);
-            aDialog.setMessage(cseq);
-            CharSequence okSeq = getString(R.string.common_ok);
-            aDialog.setButton(okSeq, new DialogInterface.OnClickListener(){
-
-                @Override
-                public void onClick(DialogInterface dialog, int which) {
-                   return; 
-                }
-                
-            });
-            aDialog.show();
-            mPinHdr.setText(R.string.pincode_configure_your_pin);
-            mPinHdrExplanation.setVisibility(View.VISIBLE);
-            clearBoxes();
-        }
-    
-    }
-   
-    
-    protected void pinCodeEnd(boolean state){
-        AlertDialog aDialog = new AlertDialog.Builder(this).create();
-        
-        if (state){
-            CharSequence saveSeq = getString(R.string.common_save_exit);
-            aDialog.setTitle(saveSeq);
-            CharSequence cseq = getString(R.string.pincode_stored);
-            aDialog.setMessage(cseq);
-            
-        }else{
-            CharSequence saveSeq = getString(R.string.common_save_exit);
-            aDialog.setTitle(saveSeq);
-            CharSequence cseq = getString(R.string.pincode_removed);
-            aDialog.setMessage(cseq);
-            
-        }
-        CharSequence okSeq = getString(R.string.common_ok);
-        aDialog.setButton(okSeq, new DialogInterface.OnClickListener(){
-
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-                finish();
-                return; 
-            }
-            
-        });
-        aDialog.show(); 
-    }
-    
-    protected void savePincodeAndExit(){
-        SharedPreferences.Editor appPrefs = PreferenceManager
-                .getDefaultSharedPreferences(getApplicationContext()).edit();
-        
-        appPrefs.putString("PrefPinCode1", tempText[0]);
-        appPrefs.putString("PrefPinCode2",tempText[1]);
-        appPrefs.putString("PrefPinCode3", tempText[2]);
-        appPrefs.putString("PrefPinCode4", tempText[3]);
-        appPrefs.putBoolean("set_pincode",true);
-        appPrefs.commit();
-        
-        pinCodeEnd(true);
-        
-        
-        
-    }
-    
-    
-    protected void clearBoxes(){
-        
-        mText1.setText("");
-        mText2.setText("");
-        mText3.setText("");
-        mText4.setText("");
-        mText1.requestFocus(); 
-    }
-    
-    
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event){
-        if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount()== 0){
-            
-            if (activity.equals("preferences")){
-                SharedPreferences.Editor appPrefsE = PreferenceManager
-            
-                    .getDefaultSharedPreferences(getApplicationContext()).edit();
-            
-                SharedPreferences appPrefs = PreferenceManager
-                    .getDefaultSharedPreferences(getApplicationContext());
-            
-                boolean state = appPrefs.getBoolean("set_pincode", false);
-                appPrefsE.putBoolean("set_pincode",!state); 
-                appPrefsE.commit();
-                setInitVars();
-                finish();
-            }
-            return true; 
-            
-        }
-        
-        return super.onKeyDown(keyCode, event);
-    }
-    
-   
-
-    
-            
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/activity/Preferences.java b/src/de/mobilcom/debitel/cloud/android/ui/activity/Preferences.java
deleted file mode 100644 (file)
index 7b34a96..0000000
+++ /dev/null
@@ -1,359 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.activity;
-
-import java.util.Vector;
-
-import android.accounts.Account;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.net.Uri;
-import android.os.Bundle;
-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.PreferenceCategory;
-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 de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.OwnCloudSession;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-import de.mobilcom.debitel.cloud.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;
-                }
-            });            
-            
-        }
-        
-        
-
-        PreferenceCategory preferenceCategory = (PreferenceCategory) findPreference("more");
-        
-        boolean helpEnabled = getResources().getBoolean(R.bool.help_enabled);
-        Preference pHelp =  findPreference("help");
-        if (pHelp != null ){
-            if (helpEnabled) {
-                pHelp.setOnPreferenceClickListener(new OnPreferenceClickListener() {
-                    @Override
-                    public boolean onPreferenceClick(Preference preference) {
-                        String helpWeb   =(String) getText(R.string.url_help);
-                        if (helpWeb != null && helpWeb.length() > 0) {
-                            Uri uriUrl = Uri.parse(helpWeb);
-                            Intent intent = new Intent(Intent.ACTION_VIEW, uriUrl);
-                            startActivity(intent);
-                        }
-                        return true;
-                    }
-                });
-            } else {
-                preferenceCategory.removePreference(pHelp);
-            }
-            
-        }
-        
-       
-       boolean recommendEnabled = getResources().getBoolean(R.bool.recommend_enabled);
-       Preference pRecommend =  findPreference("recommend");
-        if (pRecommend != null){
-            if (recommendEnabled) {
-                pRecommend.setOnPreferenceClickListener(new OnPreferenceClickListener() {
-                    @Override
-                    public boolean onPreferenceClick(Preference preference) {
-
-                        Intent intent = new Intent(Intent.ACTION_SENDTO); 
-                        intent.setType("text/plain");
-                        Account currentAccount = AccountUtils.getCurrentOwnCloudAccount(Preferences.this);
-                        String appName = getString(R.string.app_name);
-                        String username = currentAccount.name.substring(0, currentAccount.name.lastIndexOf('@')); 
-                        String recommendSubject = String.format(getString(R.string.recommend_subject), username, appName);
-                        //String recommendSubject = String.format(getString(R.string.recommend_subject), appName);
-                        intent.putExtra(Intent.EXTRA_SUBJECT, recommendSubject);
-                        String recommendText = String.format(getString(R.string.recommend_text), getString(R.string.app_name), username);
-                        //String recommendText = String.format(getString(R.string.recommend_text), getString(R.string.app_name), getString(R.string.url_app_download));
-                        intent.putExtra(Intent.EXTRA_TEXT, recommendText);
-
-                        intent.setData(Uri.parse(getString(R.string.mail_recommend))); 
-                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
-                        startActivity(intent);
-
-
-                        return(true);
-
-                    }
-                });
-            } else {
-                preferenceCategory.removePreference(pRecommend);
-            }
-            
-        }
-        
-        boolean feedbackEnabled = getResources().getBoolean(R.bool.feedback_enabled);
-        Preference pFeedback =  findPreference("feedback");
-        if (pFeedback != null){
-            if (feedbackEnabled) {
-                pFeedback.setOnPreferenceClickListener(new OnPreferenceClickListener() {
-                    @Override
-                    public boolean onPreferenceClick(Preference preference) {
-                        String feedbackMail   =(String) getText(R.string.mail_feedback);
-                        String feedback   =(String) getText(R.string.prefs_feedback);
-                        Intent intent = new Intent(Intent.ACTION_SENDTO); 
-                        intent.setType("text/plain");
-                        intent.putExtra(Intent.EXTRA_SUBJECT, feedback);
-                        
-                        intent.setData(Uri.parse(feedbackMail)); 
-                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
-                        startActivity(intent);
-                        
-                        return true;
-                    }
-                });
-            } else {
-                preferenceCategory.removePreference(pFeedback);
-            }
-            
-        }
-        
-        boolean imprintEnabled = getResources().getBoolean(R.bool.imprint_enabled);
-        Preference pImprint =  findPreference("imprint");
-        if (pImprint != null) {
-            if (imprintEnabled) {
-                pImprint.setOnPreferenceClickListener(new OnPreferenceClickListener() {
-                    @Override
-                    public boolean onPreferenceClick(Preference preference) {
-                        String imprintWeb = (String) getText(R.string.url_imprint);
-                        if (imprintWeb != null && imprintWeb.length() > 0) {
-                            Uri uriUrl = Uri.parse(imprintWeb);
-                            Intent intent = new Intent(Intent.ACTION_VIEW, uriUrl);
-                            startActivity(intent);
-                        }
-                        //ImprintDialog.newInstance(true).show(preference.get, "IMPRINT_DIALOG");
-                        return true;
-                    }
-                });
-            } else {
-                preferenceCategory.removePreference(pImprint);
-            }
-        }
-            
-        /* 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);
-               }
-       }
-       
-       /* DISABLED FOR RELEASE UNTIL FIXED 
-       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("de.mobilcom.debitel.cloud.android.location.LocationLauncher");
-            locationServiceIntent.putExtra("TRACKING_SETTING",
-                    (Boolean) newValue);
-            sendBroadcast(locationServiceIntent);
-        } 
-        return true;
-    }
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/activity/PreferencesNewSession.java b/src/de/mobilcom/debitel/cloud/android/ui/activity/PreferencesNewSession.java
deleted file mode 100644 (file)
index d78b1e9..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-/* 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 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 de.mobilcom.debitel.cloud.android.ui.activity;\r
-\r
-import android.accounts.AccountAuthenticatorActivity;\r
-import android.app.Activity;\r
-import android.os.Bundle;\r
-import android.view.View;\r
-import android.view.View.OnClickListener;\r
-\r
-public class PreferencesNewSession extends AccountAuthenticatorActivity\r
-        implements OnClickListener {\r
-    @Override\r
-    public void onCreate(Bundle savedInstanceState) {\r
-        super.onCreate(savedInstanceState);\r
-        // setContentView(R.layout.add_new_session);\r
-        /*\r
-         * EditText et;// = (EditText)\r
-         * findViewById(R.id.newSession_sessionName);\r
-         * \r
-         * et = (EditText) findViewById(R.id.newSession_URL); if\r
-         * (getIntent().hasExtra("sessionURL")) { try { URI uri = new\r
-         * URI(getIntent().getStringExtra("sessionURL")); String url =\r
-         * uri.getHost(); if (uri.getPort() != -1) { url += ":" +\r
-         * String.valueOf(uri.getPort()); } if (uri.getPath() != null) { url +=\r
-         * uri.getPath(); } else { url += "/"; } et.setText(url); et =\r
-         * (EditText) findViewById(R.id.newSession_username); if\r
-         * (uri.getAuthority() != null) { if (uri.getUserInfo().indexOf(':') !=\r
-         * -1) { et.setText(uri.getUserInfo().substring(0,\r
-         * uri.getUserInfo().indexOf(':'))); et = (EditText)\r
-         * findViewById(R.id.newSession_password);\r
-         * et.setText(uri.getUserInfo().substring\r
-         * (uri.getUserInfo().indexOf(':')+1)); } else {\r
-         * et.setText(uri.getUserInfo()); } }\r
-         * \r
-         * } catch (URISyntaxException e) { Log.e(TAG, "Incorrect URI syntax " +\r
-         * e.getLocalizedMessage()); } }\r
-         * \r
-         * mReturnData = new Intent(); setResult(Activity.RESULT_OK,\r
-         * mReturnData); ((Button)\r
-         * findViewById(R.id.button1)).setOnClickListener(this); ((Button)\r
-         * findViewById(R.id.button2)).setOnClickListener(this);\r
-         */\r
-    }\r
-\r
-    @Override\r
-    protected void onResume() {\r
-        super.onResume();\r
-    }\r
-\r
-    public void onClick(View v) {\r
-        /*\r
-         * switch (v.getId()) { case R.id.button1: Intent intent = new Intent();\r
-         * if (getIntent().hasExtra("sessionId")) { intent.putExtra("sessionId",\r
-         * getIntent().getIntExtra("sessionId", -1)); } //String sessionName =\r
-         * ((EditText)\r
-         * findViewById(R.id.newSession_sessionName)).getText().toString(); //\r
-         * if (sessionName.trim().equals("") || !isNameValid(sessionName)) { //\r
-         * Toast.makeText(this, R.string.new_session_session_name_error,\r
-         * Toast.LENGTH_LONG).show(); // break; // } URI uri = prepareURI(); if\r
-         * (uri != null) { //intent.putExtra("sessionName", sessionName);\r
-         * intent.putExtra("sessionURL", uri.toString());\r
-         * setResult(Activity.RESULT_OK, intent); AccountManager accMgr =\r
-         * AccountManager.get(this); Account a = new Account("OwnCloud",\r
-         * AccountAuthenticatorService.ACCOUNT_TYPE);\r
-         * accMgr.addAccountExplicitly(a, "asd", null); finish(); } break; case\r
-         * R.id.button2: setResult(Activity.RESULT_CANCELED); finish(); break; }\r
-         */\r
-    }\r
-\r
-    /*\r
-     * private URI prepareURI() { URI uri = null; String url = ""; try { String\r
-     * username = ((EditText)\r
-     * findViewById(R.id.newSession_username)).getText().toString().trim();\r
-     * String password = ((EditText)\r
-     * findViewById(R.id.newSession_password)).getText().toString().trim();\r
-     * String hostname = ((EditText)\r
-     * findViewById(R.id.newSession_URL)).getText().toString().trim(); String\r
-     * scheme; if (hostname.matches("[A-Za-z]://")) { scheme =\r
-     * hostname.substring(0, hostname.indexOf("://")+3); hostname =\r
-     * hostname.substring(hostname.indexOf("://")+3); } else { scheme =\r
-     * "http://"; } if (!username.equals("")) { if (!password.equals("")) {\r
-     * username += ":" + password + "@"; } else { username += "@"; } } url =\r
-     * scheme + username + hostname; Log.i(TAG, url); uri = new URI(url); }\r
-     * catch (URISyntaxException e) { Log.e(TAG, "Incorrect URI syntax " +\r
-     * e.getLocalizedMessage()); Toast.makeText(this,\r
-     * R.string.new_session_uri_error, Toast.LENGTH_LONG).show(); } return uri;\r
-     * }\r
-     * \r
-     * private boolean isNameValid(String string) { return\r
-     * string.matches("[A-Za-z0-9 _-]*"); }\r
-     */\r
-\r
-    @Override\r
-    public void onBackPressed() {\r
-        setResult(Activity.RESULT_CANCELED);\r
-        super.onBackPressed();\r
-    }\r
-\r
-}\r
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/activity/TransferServiceGetter.java b/src/de/mobilcom/debitel/cloud/android/ui/activity/TransferServiceGetter.java
deleted file mode 100644 (file)
index acf17e5..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.activity;
-
-import de.mobilcom.debitel.cloud.android.files.services.FileDownloader.FileDownloaderBinder;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader.FileUploaderBinder;
-
-public interface TransferServiceGetter {
-
-    /**
-     * Callback method invoked when the parent activity is fully created to get a reference to the FileDownloader service API.
-     * 
-     * @return  Directory to list firstly. Can be NULL.
-     */
-    public FileDownloaderBinder getFileDownloaderBinder();
-
-    
-    /**
-     * Callback method invoked when the parent activity is fully created to get a reference to the FileUploader service API.
-     * 
-     * @return  Directory to list firstly. Can be NULL.
-     */
-    public FileUploaderBinder getFileUploaderBinder();
-
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/activity/UploadFilesActivity.java b/src/de/mobilcom/debitel/cloud/android/ui/activity/UploadFilesActivity.java
deleted file mode 100644 (file)
index 6d0c88d..0000000
+++ /dev/null
@@ -1,395 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.activity;
-
-import java.io.File;
-
-import android.accounts.Account;
-import android.content.Intent;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.Environment;
-import android.support.v4.app.DialogFragment;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.TextView;
-
-import com.actionbarsherlock.app.ActionBar;
-import com.actionbarsherlock.app.ActionBar.OnNavigationListener;
-import com.actionbarsherlock.view.MenuItem;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.ui.CustomButton;
-import de.mobilcom.debitel.cloud.android.ui.dialog.IndeterminateProgressDialog;
-import de.mobilcom.debitel.cloud.android.ui.fragment.ConfirmationDialogFragment;
-import de.mobilcom.debitel.cloud.android.ui.fragment.LocalFileListFragment;
-import de.mobilcom.debitel.cloud.android.ui.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
-import de.mobilcom.debitel.cloud.android.utils.FileStorageUtils;
-
-/**
- * Displays local files and let the user choose what of them wants to upload
- * to the current ownCloud account
- * 
- * @author David A. Velasco
- * 
- */
-
-public class UploadFilesActivity extends FileActivity implements
-    LocalFileListFragment.ContainerActivity, OnNavigationListener, OnClickListener, ConfirmationDialogFragmentListener {
-    
-    private ArrayAdapter<String> mDirectories;
-    private File mCurrentDir = null;
-    private LocalFileListFragment mFileListFragment;
-    private CustomButton mCancelBtn;
-    private CustomButton mUploadBtn;
-    private Account mAccountOnCreation;
-    private DialogFragment mCurrentDialog;
-    
-    public static final String EXTRA_CHOSEN_FILES = UploadFilesActivity.class.getCanonicalName() + ".EXTRA_CHOSEN_FILES";
-
-    public static final int RESULT_OK_AND_MOVE = RESULT_FIRST_USER; 
-    
-    private static final String KEY_DIRECTORY_PATH = UploadFilesActivity.class.getCanonicalName() + ".KEY_DIRECTORY_PATH";
-    private static final String TAG = "UploadFilesActivity";
-    private static final String WAIT_DIALOG_TAG = "WAIT";
-    private static final String QUERY_TO_MOVE_DIALOG_TAG = "QUERY_TO_MOVE";
-    
-    
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        Log_OC.d(TAG, "onCreate() start");
-        super.onCreate(savedInstanceState);
-
-        if(savedInstanceState != null) {
-            mCurrentDir = new File(savedInstanceState.getString(UploadFilesActivity.KEY_DIRECTORY_PATH));
-        } else {
-            mCurrentDir = Environment.getExternalStorageDirectory();
-        }
-        
-        mAccountOnCreation = getAccount();
-                
-        /// USER INTERFACE
-            
-        // Drop-down navigation 
-        mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
-        File currDir = mCurrentDir;
-        while(currDir != null && currDir.getParentFile() != null) {
-            mDirectories.add(currDir.getName());
-            currDir = currDir.getParentFile();
-        }
-        mDirectories.add(File.separator);
-
-        // Inflate and set the layout view
-        setContentView(R.layout.upload_files_layout);
-        mFileListFragment = (LocalFileListFragment) getSupportFragmentManager().findFragmentById(R.id.local_files_list);
-        
-        
-        // Set input controllers
-        mCancelBtn = (CustomButton) findViewById(R.id.upload_files_btn_cancel);
-        mCancelBtn.setOnClickListener(this);
-        mUploadBtn = (CustomButton) findViewById(R.id.upload_files_btn_upload);
-        mUploadBtn.setOnClickListener(this);
-        
-            
-        // Action bar setup
-        ActionBar actionBar = getSupportActionBar();
-        actionBar.setHomeButtonEnabled(true);   // mandatory since Android ICS, according to the official documentation
-        actionBar.setDisplayHomeAsUpEnabled(mCurrentDir != null && mCurrentDir.getName() != null);
-        actionBar.setDisplayShowTitleEnabled(false);
-        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
-        actionBar.setListNavigationCallbacks(mDirectories, this);
-        
-        // wait dialog
-        if (mCurrentDialog != null) {
-            mCurrentDialog.dismiss();
-            mCurrentDialog = null;
-        }
-            
-        Log_OC.d(TAG, "onCreate() end");
-    }
-
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        boolean retval = true;
-        switch (item.getItemId()) {
-            case android.R.id.home: {
-                if(mCurrentDir != null && mCurrentDir.getParentFile() != null){
-                    onBackPressed(); 
-                }
-                break;
-            }
-            default:
-                retval = super.onOptionsItemSelected(item);
-        }
-        return retval;
-    }
-
-    
-    @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;
-    }
-
-    
-    @Override
-    public void onBackPressed() {
-        if (mDirectories.getCount() <= 1) {
-            finish();
-            return;
-        }
-        popDirname();
-        mFileListFragment.onNavigateUp();
-        mCurrentDir = mFileListFragment.getCurrentDirectory();
-        
-        if(mCurrentDir.getParentFile() == null){
-            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(TAG, "onSaveInstanceState() start");
-        super.onSaveInstanceState(outState);
-        outState.putString(UploadFilesActivity.KEY_DIRECTORY_PATH, mCurrentDir.getAbsolutePath());
-        Log_OC.d(TAG, "onSaveInstanceState() end");
-    }
-
-    
-    /**
-     * Pushes a directory to the drop down list
-     * @param directory to push
-     * @throws IllegalArgumentException If the {@link File#isDirectory()} returns false.
-     */
-    public void pushDirname(File directory) {
-        if(!directory.isDirectory()){
-            throw new IllegalArgumentException("Only directories may be pushed!");
-        }
-        mDirectories.insert(directory.getName(), 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(UploadFilesActivity 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;
-        }
-    
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onDirectoryClick(File directory) {
-        pushDirname(directory);
-        ActionBar actionBar = getSupportActionBar();
-        actionBar.setDisplayHomeAsUpEnabled(true);
-    }
-    
-    
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onFileClick(File file) {
-        // nothing to do
-    }
-    
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public File getInitialDirectory() {
-        return mCurrentDir;
-    }
-
-
-    /**
-     * Performs corresponding action when user presses 'Cancel' or 'Upload' button
-     * 
-     * TODO Make here the real request to the Upload service ; will require to receive the account and 
-     * target folder where the upload must be done in the received intent.
-     */
-    @Override
-    public void onClick(View v) {
-        if (v.getId() == R.id.upload_files_btn_cancel) {
-            setResult(RESULT_CANCELED);
-            finish();
-            
-        } else if (v.getId() == R.id.upload_files_btn_upload) {
-            new CheckAvailableSpaceTask().execute();
-        }
-    }
-
-
-    /**
-     * Asynchronous task checking if there is space enough to copy all the files chosen
-     * to upload into the ownCloud local folder.
-     * 
-     * Maybe an AsyncTask is not strictly necessary, but who really knows.
-     * 
-     * @author David A. Velasco
-     */
-    private class CheckAvailableSpaceTask extends AsyncTask<Void, Void, Boolean> {
-
-        /**
-         * Updates the UI before trying the movement
-         */
-        @Override
-        protected void onPreExecute () {
-            /// progress dialog and disable 'Move' button
-            mCurrentDialog = IndeterminateProgressDialog.newInstance(R.string.wait_a_moment, false);
-            mCurrentDialog.show(getSupportFragmentManager(), WAIT_DIALOG_TAG);
-        }
-        
-        
-        /**
-         * Checks the available space
-         * 
-         * @return     'True' if there is space enough.
-         */
-        @Override
-        protected Boolean doInBackground(Void... params) {
-            String[] checkedFilePaths = mFileListFragment.getCheckedFilePaths();
-            long total = 0;
-            for (int i=0; checkedFilePaths != null && i < checkedFilePaths.length ; i++) {
-                String localPath = checkedFilePaths[i];
-                File localFile = new File(localPath);
-                total += localFile.length();
-            }
-            return (FileStorageUtils.getUsableSpace(mAccountOnCreation.name) >= total);
-        }
-
-        /**
-         * Updates the activity UI after the check of space is done.
-         * 
-         * If there is not space enough. shows a new dialog to query the user if wants to move the files instead
-         * of copy them.
-         * 
-         * @param result        'True' when there is space enough to copy all the selected files.
-         */
-        @Override
-        protected void onPostExecute(Boolean result) {
-            mCurrentDialog.dismiss();
-            mCurrentDialog = null;
-            
-            if (result) {
-                // return the list of selected files (success)
-                Intent data = new Intent();
-                data.putExtra(EXTRA_CHOSEN_FILES, mFileListFragment.getCheckedFilePaths());
-                setResult(RESULT_OK, data);
-                finish();
-                
-            } else {
-                // show a dialog to query the user if wants to move the selected files to the ownCloud folder instead of copying
-                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);
-                dialog.show(getSupportFragmentManager(), QUERY_TO_MOVE_DIALOG_TAG);
-            }
-        }
-    }
-
-    @Override
-    public void onConfirmation(String 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();
-            data.putExtra(EXTRA_CHOSEN_FILES, mFileListFragment.getCheckedFilePaths());
-            setResult(RESULT_OK_AND_MOVE, data);
-            finish();
-        }
-    }
-
-
-    @Override
-    public void onNeutral(String callerTag) {
-        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_OC.d(TAG, "Negative button in dialog was clicked; dialog tag is " + callerTag);
-    }
-
-
-    @Override
-    protected void onAccountSet(boolean stateWasRecovered) {
-        if (getAccount() != null) {
-            if (!mAccountOnCreation.equals(getAccount())) {
-                setResult(RESULT_CANCELED);
-                finish();
-            }
-            
-        } else {
-            Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
-            setResult(RESULT_CANCELED);
-            finish();
-        }
-    }    
-
-    
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/adapter/FileListListAdapter.java b/src/de/mobilcom/debitel/cloud/android/ui/adapter/FileListListAdapter.java
deleted file mode 100644 (file)
index 5ab922c..0000000
+++ /dev/null
@@ -1,209 +0,0 @@
-/* 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 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
-package de.mobilcom.debitel.cloud.android.ui.adapter;\r
-\r
-import android.accounts.Account;\r
-import android.content.Context;\r
-import android.view.LayoutInflater;\r
-import android.view.View;\r
-import android.view.ViewGroup;\r
-import android.widget.BaseAdapter;\r
-import android.widget.ImageView;\r
-import android.widget.ListAdapter;\r
-import android.widget.ListView;\r
-import android.widget.TextView;\r
-\r
-import de.mobilcom.debitel.cloud.android.DisplayUtils;\r
-import de.mobilcom.debitel.cloud.android.R;\r
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;\r
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;\r
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;\r
-import de.mobilcom.debitel.cloud.android.files.services.FileDownloader.FileDownloaderBinder;\r
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader.FileUploaderBinder;\r
-import de.mobilcom.debitel.cloud.android.ui.activity.TransferServiceGetter;\r
-\r
-import java.util.Vector;\r
-\r
-\r
-/**\r
- * This Adapter populates a ListView with all files and folders in an ownCloud\r
- * instance.\r
- * \r
- * @author Bartek Przybylski\r
- * \r
- */\r
-public class FileListListAdapter extends BaseAdapter implements ListAdapter {\r
-    private Context mContext;\r
-    private OCFile mFile = null;\r
-    private Vector<OCFile> mFiles = null;\r
-    private DataStorageManager mStorageManager;\r
-    private Account mAccount;\r
-    private TransferServiceGetter mTransferServiceGetter;\r
-    \r
-    public FileListListAdapter(Context context, TransferServiceGetter transferServiceGetter) {\r
-        mContext = context;\r
-        mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);\r
-        mTransferServiceGetter = transferServiceGetter;\r
-    }\r
-\r
-    @Override\r
-    public boolean areAllItemsEnabled() {\r
-        return true;\r
-    }\r
-\r
-    @Override\r
-    public boolean isEnabled(int position) {\r
-        return true;\r
-    }\r
-\r
-    @Override\r
-    public int getCount() {\r
-        return mFiles != null ? mFiles.size() : 0;\r
-    }\r
-\r
-    @Override\r
-    public Object getItem(int position) {\r
-        if (mFiles == null || mFiles.size() <= position)\r
-            return null;\r
-        return mFiles.get(position);\r
-    }\r
-\r
-    @Override\r
-    public long getItemId(int position) {\r
-        if (mFiles == null || mFiles.size() <= position)\r
-            return 0;\r
-        return mFiles.get(position).getFileId();\r
-    }\r
-\r
-    @Override\r
-    public int getItemViewType(int position) {\r
-        return 0;\r
-    }\r
-\r
-    @Override\r
-    public View getView(int position, View convertView, ViewGroup parent) {\r
-        View view = convertView;\r
-        if (view == null) {\r
-            LayoutInflater inflator = (LayoutInflater) mContext\r
-                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);\r
-            view = inflator.inflate(R.layout.list_item, null);\r
-        }\r
-    \r
-        if (mFiles != null && mFiles.size() > position) {\r
-            OCFile file = mFiles.get(position);\r
-            TextView fileName = (TextView) view.findViewById(R.id.Filename);\r
-            String name = file.getFileName();\r
-\r
-            fileName.setText(name);\r
-            ImageView fileIcon = (ImageView) view.findViewById(R.id.imageView1);\r
-            fileIcon.setImageResource(DisplayUtils.getResourceId(file.getMimetype()));\r
-            ImageView localStateView = (ImageView) view.findViewById(R.id.imageView2);\r
-            FileDownloaderBinder downloaderBinder = mTransferServiceGetter.getFileDownloaderBinder();\r
-            FileUploaderBinder uploaderBinder = mTransferServiceGetter.getFileUploaderBinder();\r
-            if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) {\r
-                localStateView.setImageResource(R.drawable.downloading_file_indicator);\r
-                localStateView.setVisibility(View.VISIBLE);\r
-            } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file)) {\r
-                localStateView.setImageResource(R.drawable.uploading_file_indicator);\r
-                localStateView.setVisibility(View.VISIBLE);\r
-            } else if (file.isDown()) {\r
-                localStateView.setImageResource(R.drawable.local_file_indicator);\r
-                localStateView.setVisibility(View.VISIBLE);\r
-            } else {\r
-                localStateView.setVisibility(View.INVISIBLE);\r
-            }\r
-            \r
-            TextView fileSizeV = (TextView) view.findViewById(R.id.file_size);\r
-            TextView lastModV = (TextView) view.findViewById(R.id.last_mod);\r
-            ImageView checkBoxV = (ImageView) view.findViewById(R.id.custom_checkbox);\r
-            \r
-            if (!file.isDirectory()) {\r
-                fileSizeV.setVisibility(View.VISIBLE);\r
-                fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));\r
-                lastModV.setVisibility(View.VISIBLE);\r
-                lastModV.setText(DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp()));\r
-                // this if-else is needed even thoe fav icon is visible by default\r
-                // because android reuses views in listview\r
-                if (!file.keepInSync()) {\r
-                    view.findViewById(R.id.imageView3).setVisibility(View.GONE);\r
-                } else {\r
-                    view.findViewById(R.id.imageView3).setVisibility(View.VISIBLE);\r
-                }\r
-                \r
-                ListView parentList = (ListView)parent;\r
-                if (parentList.getChoiceMode() == ListView.CHOICE_MODE_NONE) { \r
-                    checkBoxV.setVisibility(View.GONE);\r
-                } else {\r
-                    if (parentList.isItemChecked(position)) {\r
-                        checkBoxV.setImageResource(android.R.drawable.checkbox_on_background);\r
-                    } else {\r
-                        checkBoxV.setImageResource(android.R.drawable.checkbox_off_background);\r
-                    }\r
-                    checkBoxV.setVisibility(View.VISIBLE);\r
-                }\r
-                \r
-            } \r
-            else {\r
-                \r
-                fileSizeV.setVisibility(View.VISIBLE);\r
-                fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));\r
-                lastModV.setVisibility(View.VISIBLE);\r
-                lastModV.setText(DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp()));\r
-               checkBoxV.setVisibility(View.GONE);\r
-               view.findViewById(R.id.imageView3).setVisibility(View.GONE);\r
-            }\r
-        }\r
-\r
-        return view;\r
-    }\r
-\r
-    @Override\r
-    public int getViewTypeCount() {\r
-        return 1;\r
-    }\r
-\r
-    @Override\r
-    public boolean hasStableIds() {\r
-        return true;\r
-    }\r
-\r
-    @Override\r
-    public boolean isEmpty() {\r
-        return (mFiles == null || mFiles.isEmpty());\r
-    }\r
-\r
-    /**\r
-     * Change the adapted directory for a new one\r
-     * @param directory                 New file to adapt. Can be NULL, meaning "no content to adapt".\r
-     * @param updatedStorageManager     Optional updated storage manager; used to replace mStorageManager if is different (and not NULL)\r
-     */\r
-    public void swapDirectory(OCFile directory, DataStorageManager updatedStorageManager) {\r
-        mFile = directory;\r
-        if (updatedStorageManager != null && updatedStorageManager != mStorageManager) {\r
-            mStorageManager = updatedStorageManager;\r
-            mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);\r
-        }\r
-        if (mStorageManager != null) {\r
-            mFiles = mStorageManager.getDirectoryContent(mFile);\r
-        } else {\r
-            mFiles = null;\r
-        }\r
-        notifyDataSetChanged();\r
-    }\r
-    \r
-}\r
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/adapter/LandingScreenAdapter.java b/src/de/mobilcom/debitel/cloud/android/ui/adapter/LandingScreenAdapter.java
deleted file mode 100644 (file)
index 42491f8..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-/* 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 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
-package de.mobilcom.debitel.cloud.android.ui.adapter;\r
-\r
-\r
-import android.content.Context;\r
-import android.content.Intent;\r
-import android.view.LayoutInflater;\r
-import android.view.View;\r
-import android.view.ViewGroup;\r
-import android.widget.BaseAdapter;\r
-import android.widget.ImageView;\r
-import android.widget.TextView;\r
-import de.mobilcom.debitel.cloud.android.R;\r
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;\r
-import de.mobilcom.debitel.cloud.android.ui.activity.FileDisplayActivity;\r
-import de.mobilcom.debitel.cloud.android.ui.activity.Preferences;\r
-\r
-/**\r
- * Populates the landing screen icons.\r
- * \r
- * @author Lennart Rosam\r
- * \r
- */\r
-public class LandingScreenAdapter extends BaseAdapter {\r
-\r
-    private Context mContext;\r
-\r
-    private final Integer[] mLandingScreenIcons = { R.drawable.home,\r
-            R.drawable.music, R.drawable.contacts, R.drawable.calendar,\r
-            android.R.drawable.ic_menu_agenda, R.drawable.settings };\r
-\r
-    private final Integer[] mLandingScreenTexts = { R.string.main_files,\r
-            R.string.main_music, R.string.main_contacts,\r
-            R.string.main_calendar, R.string.main_bookmarks,\r
-            R.string.main_settings };\r
-\r
-    public LandingScreenAdapter(Context context) {\r
-        mContext = context;\r
-    }\r
-\r
-    @Override\r
-    public int getCount() {\r
-        return mLandingScreenIcons.length;\r
-    }\r
-\r
-    @Override\r
-    /**\r
-     * Returns the Intent associated with this object\r
-     * or null if the functionality is not yet implemented\r
-     */\r
-    public Object getItem(int position) {\r
-        Intent intent = new Intent();\r
-\r
-        switch (position) {\r
-        case 0:\r
-            /*\r
-             * The FileDisplayActivity requires the ownCloud account as an\r
-             * parcableExtra. We will put in the one that is selected in the\r
-             * preferences\r
-             */\r
-            intent.setClass(mContext, FileDisplayActivity.class);\r
-            intent.putExtra("ACCOUNT",\r
-                    AccountUtils.getCurrentOwnCloudAccount(mContext));\r
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\r
-            break;\r
-        case 5:\r
-            intent.setClass(mContext, Preferences.class);\r
-            break;\r
-        default:\r
-            intent = null;\r
-        }\r
-        return intent;\r
-    }\r
-\r
-    @Override\r
-    public long getItemId(int position) {\r
-        return position;\r
-    }\r
-\r
-    @Override\r
-    public View getView(int position, View convertView, ViewGroup parent) {\r
-        if (convertView == null) {\r
-            LayoutInflater inflator = LayoutInflater.from(mContext);\r
-            convertView = inflator.inflate(R.layout.landing_page_item, null);\r
-\r
-            ImageView icon = (ImageView) convertView\r
-                    .findViewById(R.id.gridImage);\r
-            TextView iconText = (TextView) convertView\r
-                    .findViewById(R.id.gridText);\r
-\r
-            icon.setImageResource(mLandingScreenIcons[position]);\r
-            iconText.setText(mLandingScreenTexts[position]);\r
-        }\r
-        return convertView;\r
-    }\r
-}\r
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/adapter/LocalFileListAdapter.java b/src/de/mobilcom/debitel/cloud/android/ui/adapter/LocalFileListAdapter.java
deleted file mode 100644 (file)
index 9b37577..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.adapter;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.Comparator;
-
-import de.mobilcom.debitel.cloud.android.DisplayUtils;
-import de.mobilcom.debitel.cloud.android.R;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
-
-/**
- * This Adapter populates a ListView with all files and directories contained
- * in a local directory
- * 
- * @author David A. Velasco
- * 
- */
-public class LocalFileListAdapter extends BaseAdapter implements ListAdapter {
-    
-    private Context mContext;
-    private File mDirectory;
-    private File[] mFiles = null;
-
-    public LocalFileListAdapter(File directory, Context context) {
-        mContext = context;
-        swapDirectory(directory);
-    }
-
-    @Override
-    public boolean areAllItemsEnabled() {
-        return true;
-    }
-
-    @Override
-    public boolean isEnabled(int position) {
-        return true;
-    }
-
-    @Override
-    public int getCount() {
-        return mFiles != null ? mFiles.length : 0;
-    }
-
-    @Override
-    public Object getItem(int position) {
-        if (mFiles == null || mFiles.length <= position)
-            return null;
-        return mFiles[position];
-    }
-
-    @Override
-    public long getItemId(int position) {
-        return mFiles != null && mFiles.length <= position ? position : -1;
-    }
-
-    @Override
-    public int getItemViewType(int position) {
-        return 0;
-    }
-
-    @Override
-    public View getView(int position, View convertView, ViewGroup parent) {
-        View view = convertView;
-        if (view == null) {
-            LayoutInflater inflator = (LayoutInflater) mContext
-                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-            view = inflator.inflate(R.layout.list_item, null);
-        }
-        if (mFiles != null && mFiles.length > position) {
-            File file = mFiles[position];
-            
-            TextView fileName = (TextView) view.findViewById(R.id.Filename);
-            String name = file.getName();
-            fileName.setText(name);
-            
-            ImageView fileIcon = (ImageView) view.findViewById(R.id.imageView1);
-            if (!file.isDirectory()) {
-                fileIcon.setImageResource(R.drawable.file);
-            } else {
-                fileIcon.setImageResource(R.drawable.ic_menu_archive);
-            }
-
-            TextView fileSizeV = (TextView) view.findViewById(R.id.file_size);
-            TextView lastModV = (TextView) view.findViewById(R.id.last_mod);
-            ImageView checkBoxV = (ImageView) view.findViewById(R.id.custom_checkbox);
-            if (!file.isDirectory()) {
-                fileSizeV.setVisibility(View.VISIBLE);
-                fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.length()));
-                lastModV.setVisibility(View.VISIBLE);
-                lastModV.setText(DisplayUtils.unixTimeToHumanReadable(file.lastModified()));
-                ListView parentList = (ListView)parent;
-                if (parentList.getChoiceMode() == ListView.CHOICE_MODE_NONE) { 
-                    checkBoxV.setVisibility(View.GONE);
-                } else {
-                    if (parentList.isItemChecked(position)) {
-                        checkBoxV.setImageResource(android.R.drawable.checkbox_on_background);
-                    } else {
-                        checkBoxV.setImageResource(android.R.drawable.checkbox_off_background);
-                    }
-                    checkBoxV.setVisibility(View.VISIBLE);
-                }
-
-            } else {
-                fileSizeV.setVisibility(View.GONE);
-                lastModV.setVisibility(View.GONE);
-                checkBoxV.setVisibility(View.GONE);
-            }
-            
-            view.findViewById(R.id.imageView2).setVisibility(View.INVISIBLE);   // not GONE; the alignment changes; ugly way to keep it
-            view.findViewById(R.id.imageView3).setVisibility(View.GONE);
-        }
-
-        return view;
-    }
-
-    @Override
-    public int getViewTypeCount() {
-        return 1;
-    }
-
-    @Override
-    public boolean hasStableIds() {
-        return false;
-    }
-
-    @Override
-    public boolean isEmpty() {
-        return (mFiles == null || mFiles.length == 0);
-    }
-
-    /**
-     * Change the adapted directory for a new one
-     * @param directory     New file to adapt. Can be NULL, meaning "no content to adapt".
-     */
-    public void swapDirectory(File directory) {
-        mDirectory = directory;
-        mFiles = (mDirectory != null ? mDirectory.listFiles() : null);
-        if (mFiles != null) {
-            Arrays.sort(mFiles, new Comparator<File>() {
-                @Override
-                public int compare(File lhs, File rhs) {
-                    if (lhs.isDirectory() && !rhs.isDirectory()) {
-                        return -1;
-                    } else if (!lhs.isDirectory() && rhs.isDirectory()) {
-                        return 1;
-                    }
-                    return compareNames(lhs, rhs);
-                }
-            
-                private int compareNames(File lhs, File rhs) {
-                    return lhs.getName().toLowerCase().compareTo(rhs.getName().toLowerCase());                
-                }
-            
-            });
-        }
-        notifyDataSetChanged();
-    }
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/adapter/LogListAdapter.java b/src/de/mobilcom/debitel/cloud/android/ui/adapter/LogListAdapter.java
deleted file mode 100644 (file)
index a0a9082..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-package de.mobilcom.debitel.cloud.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 de.mobilcom.debitel.cloud.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;
-    }
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/dialog/ChangelogDialog.java b/src/de/mobilcom/debitel/cloud/android/ui/dialog/ChangelogDialog.java
deleted file mode 100644 (file)
index 0ba97d9..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.dialog;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.webkit.WebView;
-
-import com.actionbarsherlock.app.SherlockDialogFragment;
-
-import de.mobilcom.debitel.cloud.android.R;
-
-/**
- * Dialog to show the contents of res/raw/CHANGELOG.txt
- */
-public class ChangelogDialog extends SherlockDialogFragment {
-
-    private static final String ARG_CANCELABLE = ChangelogDialog.class.getCanonicalName() + ".ARG_CANCELABLE";
-
-
-    /**
-     * Public factory method to get dialog instances.
-     * 
-     * @param cancelable    If 'true', the dialog can be cancelled by the user input (BACK button, touch outside...)
-     * @return              New dialog instance, ready to show.
-     */
-    public static ChangelogDialog newInstance(boolean cancelable) {
-        ChangelogDialog fragment = new ChangelogDialog();
-        Bundle args = new Bundle();
-        args.putBoolean(ARG_CANCELABLE, cancelable);
-        fragment.setArguments(args);
-        return fragment;
-    }
-
-    
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        /// load the custom view to insert in the dialog, between title and 
-        WebView webview = new WebView(getActivity());
-        webview.loadUrl("file:///android_res/raw/" + getResources().getResourceEntryName(R.raw.changelog) + ".html");
-        
-        /// build the dialog
-        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
-        
-        Dialog dialog = builder.setView(webview)
-                                .setIcon(R.drawable.icon)
-                                //.setTitle(R.string.whats_new)
-                                .setPositiveButton(R.string.common_ok,
-                                        new DialogInterface.OnClickListener() {
-                                    @Override
-                                    public void onClick(DialogInterface dialog, int which) {
-                                        dialog.dismiss();
-                                    }
-                                }).create();
-        
-        dialog.setCancelable(getArguments().getBoolean(ARG_CANCELABLE));
-        return dialog;
-    } 
-    
-    /**
-     * {@inheritDoc}
-     *-/
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        /// load the custom layout
-        View view = inflater.inflate(R.layout.fragment_changelog, container);
-        mEditText = (EditText) view.findViewById(R.id.txt_your_name);
-        getDialog().setTitle(R.string.whats_new);
-        
-        /// read full contents of the change log file (don't make it too big)
-        InputStream changeLogStream = getResources().openRawResource(R.raw.changelog);
-        Scanner scanner = new java.util.Scanner(changeLogStream).useDelimiter("\\A");
-        String text = scanner.hasNext() ? scanner.next() : "";
-        
-        /// make clickable the links in the change log file
-        SpannableString sText = new SpannableString(text);
-        Linkify.addLinks(sText, Linkify.ALL);
-
-        return view;
-    }
-    */
-}
-
-
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/dialog/ConflictsResolveDialog.java b/src/de/mobilcom/debitel/cloud/android/ui/dialog/ConflictsResolveDialog.java
deleted file mode 100644 (file)
index 7d7bc4a..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.dialog;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentTransaction;
-
-import com.actionbarsherlock.app.SherlockDialogFragment;
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-
-import de.mobilcom.debitel.cloud.android.R;
-
-/**
- * Dialog which will be displayed to user upon keep-in-sync file conflict.
- * 
- * @author Bartek Przybylski
- *
- */
-public class ConflictsResolveDialog extends SherlockDialogFragment {
-
-    public static enum Decision { 
-        CANCEL,
-        KEEP_BOTH,
-        OVERWRITE
-    }
-    
-    OnConflictDecisionMadeListener mListener;
-    
-    public static ConflictsResolveDialog newInstance(String path, OnConflictDecisionMadeListener listener) {
-        ConflictsResolveDialog f = new ConflictsResolveDialog();
-        Bundle args = new Bundle();
-        args.putString("remotepath", path);
-        f.setArguments(args);
-        f.mListener = listener;
-        return f;
-    }
-    
-    @Override
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        String remotepath = getArguments().getString("remotepath");
-        return new AlertDialog.Builder(getSherlockActivity())
-                   .setIcon(R.drawable.icon)
-                   .setTitle(R.string.conflict_title)
-                   .setMessage(String.format(getString(R.string.conflict_message), remotepath))
-                   .setPositiveButton(R.string.conflict_overwrite,
-                       new DialogInterface.OnClickListener() {
-
-                           @Override
-                           public void onClick(DialogInterface dialog, int which) {
-                               if (mListener != null)
-                                   mListener.ConflictDecisionMade(Decision.OVERWRITE);
-                           }
-                       })
-                   .setNeutralButton(R.string.conflict_keep_both,
-                       new DialogInterface.OnClickListener() {
-                            @Override
-                            public void onClick(DialogInterface dialog, int which) {
-                                if (mListener != null)
-                                    mListener.ConflictDecisionMade(Decision.KEEP_BOTH);
-                            }
-                        })
-                   .setNegativeButton(R.string.conflict_dont_upload,
-                       new DialogInterface.OnClickListener() {
-                           @Override
-                           public void onClick(DialogInterface dialog, int which) {
-                               if (mListener != null)
-                                   mListener.ConflictDecisionMade(Decision.CANCEL);
-                           }
-                   })
-                   .create();
-    }
-    
-    public void showDialog(SherlockFragmentActivity activity) {
-        Fragment prev = activity.getSupportFragmentManager().findFragmentByTag("dialog");
-        FragmentTransaction ft = activity.getSupportFragmentManager().beginTransaction();
-        if (prev != null) {
-            ft.remove(prev);
-        }
-        ft.addToBackStack(null);
-
-        this.show(ft, "dialog");
-    }
-
-    public void dismissDialog(SherlockFragmentActivity activity) {
-        Fragment prev = activity.getSupportFragmentManager().findFragmentByTag(getTag());
-        if (prev != null) {
-            FragmentTransaction ft = activity.getSupportFragmentManager().beginTransaction();
-            ft.remove(prev);
-            ft.commit();
-        }
-    }
-    
-    @Override
-    public void onCancel(DialogInterface dialog) {
-        mListener.ConflictDecisionMade(Decision.CANCEL);
-    }
-    
-    public interface OnConflictDecisionMadeListener {
-        public void ConflictDecisionMade(Decision decision);
-    }
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/dialog/EditNameDialog.java b/src/de/mobilcom/debitel/cloud/android/ui/dialog/EditNameDialog.java
deleted file mode 100644 (file)
index e324046..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.dialog;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.DialogInterface;
-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;
-
-import de.mobilcom.debitel.cloud.android.R;
-
-
-/**
- * Dialog to request the user to input a name, optionally initialized with a former name.
- * 
- * @author Bartek Przybylski
- * @author David A. Velasco
- */
-public class EditNameDialog extends SherlockDialogFragment implements DialogInterface.OnClickListener {
-
-    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_SELECTION_START = "SELECTION_START";
-    protected static final String ARG_SELECTION_END = "SELECTION_END";
-    
-    private String mNewFilename;
-    private boolean mResult;
-    private EditNameDialogListener mListener;
-    
-    /**
-     * 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 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, 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;
-    }
-    
-    
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        String currentName = getArguments().getString(ARG_NAME);
-        if (currentName == null)
-            currentName = "";
-        String title = getArguments().getString(ARG_TITLE);
-        
-        // 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
-        EditText inputText = ((EditText)v.findViewById(R.id.user_input));
-        inputText.setText(currentName);
-        
-        // Set it to the dialog 
-        AlertDialog.Builder builder = new AlertDialog.Builder(getSherlockActivity());
-        builder.setView(v)
-               .setPositiveButton(R.string.common_ok, this)
-               .setNegativeButton(R.string.common_cancel, this);
-
-        if (title != null) {
-            builder.setTitle(title);
-        }
-        
-        mResult = false;
-        
-        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;
-    }    
-
-    
-    /**
-     * Performs the corresponding action when a dialog button is clicked.
-     * 
-     * Saves the text in the input field to be accessed through {@link #getNewFilename()} when the positive
-     * button is clicked.
-     * 
-     * Notify the current listener in any case.
-     */
-    @Override
-    public void onClick(DialogInterface dialog, int which) {
-        switch (which) {
-            case AlertDialog.BUTTON_POSITIVE: {
-                mNewFilename = ((TextView)(getDialog().findViewById(R.id.user_input))).getText().toString();
-                mResult = true;
-            }
-            case AlertDialog.BUTTON_NEGATIVE: { // fall through
-                dismiss();
-                if (mListener != null)
-                    mListener.onDismiss(this);
-            }
-        }
-    }
-    
-    protected void setOnDismissListener(EditNameDialogListener listener) {
-        mListener = listener;
-    }
-    
-    /**
-     * Returns the text in the input field after the user clicked the positive button.
-     * 
-     * @return      Text in the input field.
-     */
-    public String getNewFilename() {
-        return mNewFilename;
-    }
-    
-    /**
-     * 
-     * @return      True when the user clicked the positive button.
-     */
-    public boolean getResult() {
-        return mResult;
-    }
-
-    
-    /**
-     * Interface to receive a notification when any button in the dialog is clicked.
-     */
-    public interface EditNameDialogListener {
-        public void onDismiss(EditNameDialog dialog);
-    }
-
-
-}
-
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/dialog/IndeterminateProgressDialog.java b/src/de/mobilcom/debitel/cloud/android/ui/dialog/IndeterminateProgressDialog.java
deleted file mode 100644 (file)
index b44c04b..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.dialog;
-
-import android.app.Dialog;
-import android.app.ProgressDialog;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnKeyListener;
-import android.os.Bundle;
-import android.view.KeyEvent;
-
-import com.actionbarsherlock.app.SherlockDialogFragment;
-
-import de.mobilcom.debitel.cloud.android.R;
-
-public class IndeterminateProgressDialog extends SherlockDialogFragment {
-
-    private static final String ARG_MESSAGE_ID = IndeterminateProgressDialog.class.getCanonicalName() + ".ARG_MESSAGE_ID";
-    private static final String ARG_CANCELABLE = IndeterminateProgressDialog.class.getCanonicalName() + ".ARG_CANCELABLE";
-
-
-    /**
-     * Public factory method to get dialog instances.
-     * 
-     * @param messageId     Resource id for a message to show in the dialog.
-     * @param cancelable    If 'true', the dialog can be cancelled by the user input (BACK button, touch outside...)
-     * @return              New dialog instance, ready to show.
-     */
-    public static IndeterminateProgressDialog newInstance(int messageId, boolean cancelable) {
-        IndeterminateProgressDialog fragment = new IndeterminateProgressDialog();
-        Bundle args = new Bundle();
-        args.putInt(ARG_MESSAGE_ID, messageId);
-        args.putBoolean(ARG_CANCELABLE, cancelable);
-        fragment.setArguments(args);
-        return fragment;
-    }
-
-    
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        /// create indeterminate progress dialog
-        final ProgressDialog dialog = new ProgressDialog(getActivity());
-        dialog.setIndeterminate(true);
-        
-        /// set message
-        int messageId = getArguments().getInt(ARG_MESSAGE_ID, R.string.placeholder_sentence);
-        dialog.setMessage(getString(messageId));
-        
-        /// set cancellation behavior
-        boolean cancelable = getArguments().getBoolean(ARG_CANCELABLE, false);
-        if (!cancelable) {
-            dialog.setCancelable(false);
-            // disable the back button
-            OnKeyListener keyListener = new OnKeyListener() {
-                @Override
-                public boolean onKey(DialogInterface dialog, int keyCode,
-                        KeyEvent event) {
-
-                    if( keyCode == KeyEvent.KEYCODE_BACK){                  
-                        return true;
-                    }
-                    return false;
-                }
-
-            };
-            dialog.setOnKeyListener(keyListener);
-        }
-        
-        return dialog;
-    }    
-    
-}
-
-
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/dialog/LoadingDialog.java b/src/de/mobilcom/debitel/cloud/android/ui/dialog/LoadingDialog.java
deleted file mode 100644 (file)
index 7394cec..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-package de.mobilcom.debitel.cloud.android.ui.dialog;
-
-import de.mobilcom.debitel.cloud.android.R;
-
-import android.app.Dialog;
-import android.os.Bundle;
-import android.support.v4.app.DialogFragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.widget.TextView;
-
-public class LoadingDialog extends DialogFragment {
-
-    private String mMessage;
-    
-    public LoadingDialog() {
-        super();
-    }
-    
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setRetainInstance(true);
-        setCancelable(false);
-    }
-
-    public LoadingDialog(String message) {
-        this.mMessage = message;
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        // Create a view by inflating desired layout
-        View v = inflater.inflate(R.layout.loading_dialog, container,  false);
-        
-        // set value
-        TextView tv  = (TextView) v.findViewById(R.id.loadingText);
-        tv.setText(mMessage);
-        
-        return v;
-    }
-
-    @Override
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        Dialog dialog = super.onCreateDialog(savedInstanceState);
-        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
-        return dialog;
-    }
-
-    @Override
-    public void onDestroyView() {
-        if (getDialog() != null && getRetainInstance())
-            getDialog().setDismissMessage(null);
-            super.onDestroyView();
-    }
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/dialog/SamlWebViewDialog.java b/src/de/mobilcom/debitel/cloud/android/ui/dialog/SamlWebViewDialog.java
deleted file mode 100644 (file)
index d897b60..0000000
+++ /dev/null
@@ -1,285 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.dialog;
-
-import android.annotation.SuppressLint;
-import android.app.Activity;
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.os.Handler;
-import android.support.v4.app.FragmentTransaction;
-import android.support.v4.app.FragmentManager;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.webkit.CookieManager;
-import android.webkit.CookieSyncManager;
-import android.webkit.WebBackForwardList;
-import android.webkit.WebSettings;
-import android.webkit.WebView;
-
-import com.actionbarsherlock.app.SherlockDialogFragment;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.authentication.SsoWebViewClient;
-import de.mobilcom.debitel.cloud.android.authentication.SsoWebViewClient.SsoWebViewClientListener;
-
-import eu.alefzero.webdav.WebdavClient;
-
-/**
- * Dialog to show the WebView for SAML Authentication
- * 
- * @author Maria Asensio
- * @author David A. Velasco
- */
-public class SamlWebViewDialog extends SherlockDialogFragment {
-
-    public final String SAML_DIALOG_TAG = "SamlWebViewDialog";
-    
-    private final static String TAG =  SamlWebViewDialog.class.getSimpleName();
-
-    private static final String ARG_INITIAL_URL = "INITIAL_URL";
-    private static final String ARG_TARGET_URL = "TARGET_URL";
-    private static final String KEY_WEBVIEW_STATE = "WEBVIEW_STATE";
-    
-    private WebView mSsoWebView;
-    private SsoWebViewClient mWebViewClient;
-    
-    private String mInitialUrl;
-    private String mTargetUrl;
-    
-    private Handler mHandler;
-
-    private SsoWebViewClientListener mSsoWebViewClientListener;
-
-    //private View mSsoRootView;
-
-
-    /**
-     * Public factory method to get dialog instances.
-     * 
-     * @param handler
-     * @param Url           Url to open at WebView
-     * @param targetURL     mHostBaseUrl + AccountUtils.getWebdavPath(mDiscoveredVersion, mCurrentAuthTokenType)
-     * @return              New dialog instance, ready to show.
-     */
-    public static SamlWebViewDialog newInstance(String url, String targetUrl) {
-        Log_OC.d(TAG, "New instance");
-        SamlWebViewDialog fragment = new SamlWebViewDialog();
-        Bundle args = new Bundle();
-        args.putString(ARG_INITIAL_URL, url);
-        args.putString(ARG_TARGET_URL, targetUrl);
-        fragment.setArguments(args);
-        return fragment;
-    }
-    
-    
-    public SamlWebViewDialog() {
-        super();
-        Log_OC.d(TAG, "constructor");
-    }
-    
-    
-    @Override
-    public void onAttach(Activity activity) {
-        Log_OC.d(TAG, "onAttach");
-        super.onAttach(activity);
-        try {
-            mSsoWebViewClientListener = (SsoWebViewClientListener) activity;
-            mHandler = new Handler();
-            mWebViewClient = new SsoWebViewClient(mHandler, mSsoWebViewClientListener);
-            
-       } catch (ClassCastException e) {
-            throw new ClassCastException(activity.toString() + " must implement " + SsoWebViewClientListener.class.getSimpleName());
-        }
-    }
-
-    
-    @SuppressLint("SetJavaScriptEnabled")
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        Log_OC.d(TAG, "onCreate");
-        super.onCreate(savedInstanceState);
-        
-        CookieSyncManager.createInstance(getActivity());
-
-        if (savedInstanceState == null) {
-            mInitialUrl = getArguments().getString(ARG_INITIAL_URL);
-            mTargetUrl = getArguments().getString(ARG_TARGET_URL);
-        } else {
-            mInitialUrl = savedInstanceState.getString(ARG_INITIAL_URL);
-            mTargetUrl = savedInstanceState.getString(ARG_TARGET_URL);
-        }
-        
-        setStyle(SherlockDialogFragment.STYLE_NO_TITLE, R.style.Theme_ownCloud_Dialog);
-    }
-    
-    @Override
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        Log_OC.d(TAG, "onCreateDialog");
-
-        /*
-        // build the dialog
-        AlertDialog.Builder builder = new AlertDialog.Builder(getSherlockActivity());
-        if (mSsoRootView.getParent() != null) {
-            ((ViewGroup)(mSsoRootView.getParent())).removeView(mSsoRootView);
-        }
-        builder.setView(mSsoRootView);
-        //builder.setView(mSsoWebView);
-        Dialog dialog = builder.create();
-        */
-        
-        return super.onCreateDialog(savedInstanceState);
-    }
-
-    @SuppressLint("SetJavaScriptEnabled")
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        Log_OC.d(TAG, "onCreateView");
-        
-        // Inflate layout of the dialog  
-        View rootView = inflater.inflate(R.layout.sso_dialog, container, false);  // null parent view because it will go in the dialog layout
-        mSsoWebView  = (WebView) rootView.findViewById(R.id.sso_webview);
-            
-        mWebViewClient.setTargetUrl(mTargetUrl);
-        mSsoWebView.setWebViewClient(mWebViewClient);
-        
-        if (savedInstanceState == null) {
-            Log_OC.d(TAG,  "   initWebView start");
-            CookieManager cookieManager = CookieManager.getInstance();
-            cookieManager.setAcceptCookie(true);
-            cookieManager.removeAllCookie();
-            mSsoWebView.loadUrl(mInitialUrl);
-            
-        } else {
-            Log_OC.d(TAG, "   restoreWebView start");
-            WebBackForwardList history = mSsoWebView.restoreState(savedInstanceState.getBundle(KEY_WEBVIEW_STATE));
-            if (history == null) {
-                Log_OC.e(TAG, "Error restoring WebView state ; back to starting URL");
-                mSsoWebView.loadUrl(mInitialUrl);
-            }
-        }
-
-        WebSettings webSettings = mSsoWebView.getSettings();
-        webSettings.setJavaScriptEnabled(true);
-        webSettings.setBuiltInZoomControls(true);
-        webSettings.setLoadWithOverviewMode(false);
-        webSettings.setSavePassword(false);
-        webSettings.setUserAgentString(WebdavClient.USER_AGENT);
-        webSettings.setSaveFormData(false);
-        
-        return rootView;
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        Log_OC.d(SAML_DIALOG_TAG, "onSaveInstanceState being CALLED");
-        super.onSaveInstanceState(outState);
-        
-        // save URLs
-        outState.putString(ARG_INITIAL_URL, mInitialUrl);
-        outState.putString(ARG_TARGET_URL, mTargetUrl);
-        
-        // Save the state of the WebView
-        Bundle webviewState = new Bundle();
-        mSsoWebView.saveState(webviewState);
-        outState.putBundle(KEY_WEBVIEW_STATE, webviewState);
-    }
-
-    @Override
-    public void onDestroyView() {
-        Log_OC.d(TAG, "onDestroyView");
-        
-        mSsoWebView.setWebViewClient(null);
-        
-        // Work around bug: http://code.google.com/p/android/issues/detail?id=17423
-        Dialog dialog = getDialog();
-        if ((dialog != null)) {
-            dialog.setOnDismissListener(null);
-            //dialog.dismiss();
-            //dialog.setDismissMessage(null);
-        }
-        
-        super.onDestroyView();
-    }
-    
-    @Override
-    public void onDestroy() {
-        Log_OC.d(TAG, "onDestroy");
-        super.onDestroy();
-    }
-
-    @Override
-    public void onDetach() {
-        Log_OC.d(TAG, "onDetach");
-        mSsoWebViewClientListener = null;
-        mWebViewClient = null;
-        super.onDetach();
-    }
-    
-    @Override
-    public void onCancel (DialogInterface dialog) {
-        Log_OC.d(SAML_DIALOG_TAG, "onCancel");
-        super.onCancel(dialog);
-    }
-    
-    @Override
-    public void onDismiss (DialogInterface dialog) {
-        Log_OC.d(SAML_DIALOG_TAG, "onDismiss");
-        super.onDismiss(dialog);
-    }
-    
-    @Override
-    public void onStart() {
-        Log_OC.d(SAML_DIALOG_TAG, "onStart");
-        super.onStart();
-    }
-
-    @Override
-    public void onStop() {
-        Log_OC.d(SAML_DIALOG_TAG, "onStop");
-        super.onStop();
-    }
-
-    @Override
-    public void onResume() {
-        Log_OC.d(SAML_DIALOG_TAG, "onResume");
-        super.onResume();
-    }
-
-    @Override
-    public void onPause() {
-        Log_OC.d(SAML_DIALOG_TAG, "onPause");
-        super.onPause();
-    }
-    
-    @Override
-    public int show (FragmentTransaction transaction, String tag) {
-        Log_OC.d(SAML_DIALOG_TAG, "show (transaction)");
-        return super.show(transaction, tag);
-    }
-
-    @Override
-    public void show (FragmentManager manager, String tag) {
-        Log_OC.d(SAML_DIALOG_TAG, "show (manager)");
-        super.show(manager, tag);
-    }
-
-}
\ No newline at end of file
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/dialog/SslValidatorDialog.java b/src/de/mobilcom/debitel/cloud/android/ui/dialog/SslValidatorDialog.java
deleted file mode 100644 (file)
index f75b984..0000000
+++ /dev/null
@@ -1,355 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.dialog;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.security.auth.x500.X500Principal;
-
-import android.app.Dialog;
-import android.content.Context;
-import android.os.Bundle;
-import android.view.View;
-import android.view.Window;
-import android.widget.TextView;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.network.CertificateCombinedException;
-import de.mobilcom.debitel.cloud.android.network.OwnCloudClientUtils;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult;
-import de.mobilcom.debitel.cloud.android.ui.CustomButton;
-
-/**
- * Dialog to request the user about a certificate that could not be validated with the certificates store in the system.
- * 
- * @author David A. Velasco
- */
-public class SslValidatorDialog extends Dialog {
-
-    private final static String TAG = SslValidatorDialog.class.getSimpleName();
-
-    private OnSslValidatorListener mListener;
-    private CertificateCombinedException mException = null;
-    private View mView;
-    
-    
-    /**
-     * Creates a new SslValidatorDialog to ask the user if an untrusted certificate from a server should
-     * be trusted.
-     * 
-     * @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. NULL if the operation can not be recovered
-     *                      by setting the certificate as reliable.
-     */
-    public static SslValidatorDialog newInstance(Context context, RemoteOperationResult result, OnSslValidatorListener listener) {
-        if (result != null && result.isSslRecoverableException()) {
-            SslValidatorDialog dialog = new SslValidatorDialog(context, listener);
-            return dialog;
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Private constructor. 
-     * 
-     * Instances have to be created through static {@link SslValidatorDialog#newInstance}.
-     * 
-     * @param context       Android context where the dialog will live
-     * @param e             Exception causing the need of prompt the user about the server certificate.
-     * @param listener      Object to notice when the server certificate was added to the local certificates store.
-     */
-    private SslValidatorDialog(Context context, OnSslValidatorListener listener) {
-        super(context);
-        mListener = listener;
-    }
-    
-    
-    /**
-     * {@inheritDoc}
-     */
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        requestWindowFeature(Window.FEATURE_NO_TITLE);
-        mView = getLayoutInflater().inflate(R.layout.ssl_validator_layout, null);
-        setContentView(mView); 
-        
-        mView.findViewById(R.id.ok).setOnClickListener( 
-                new View.OnClickListener() {
-                    @Override
-                    public void onClick(View v) {
-                        try {
-                            saveServerCert();
-                            dismiss();
-                            if (mListener != null)
-                                mListener.onSavedCertificate();
-                            else
-                                Log_OC.d(TAG, "Nobody there to notify the certificate was saved");
-                            
-                        } catch (GeneralSecurityException e) {
-                            dismiss();
-                            if (mListener != null)
-                                mListener.onFailedSavingCertificate();
-                            Log_OC.e(TAG, "Server certificate could not be saved in the known servers trust store ", e);
-                            
-                        } catch (IOException e) {
-                            dismiss();
-                            if (mListener != null)
-                                mListener.onFailedSavingCertificate();
-                            Log_OC.e(TAG, "Server certificate could not be saved in the known servers trust store ", e);
-                        }
-                    }
-                });
-        
-        mView.findViewById(R.id.cancel).setOnClickListener(
-                new View.OnClickListener() {
-                    @Override
-                    public void onClick(View v) {
-                        cancel();
-                    }
-                });
-        
-        mView.findViewById(R.id.details_btn).setOnClickListener(
-                new View.OnClickListener() {
-                   @Override
-                    public void onClick(View v) {
-                       View detailsScroll = findViewById(R.id.details_scroll);
-                       if (detailsScroll.getVisibility() == View.VISIBLE) {
-                           detailsScroll.setVisibility(View.GONE);
-                           ((CustomButton)v).setText(R.string.ssl_validator_btn_details_see);
-                           
-                       } else {
-                           detailsScroll.setVisibility(View.VISIBLE);
-                           ((CustomButton)v).setText(R.string.ssl_validator_btn_details_hide);
-                       }
-                    }
-                });
-    }
-    
-    
-    public void updateResult(RemoteOperationResult result) {
-        if (result.isSslRecoverableException()) {
-            mException = (CertificateCombinedException) result.getException();
-            
-            /// clean
-            mView.findViewById(R.id.reason_cert_not_trusted).setVisibility(View.GONE);
-            mView.findViewById(R.id.reason_cert_expired).setVisibility(View.GONE);
-            mView.findViewById(R.id.reason_cert_not_yet_valid).setVisibility(View.GONE);
-            mView.findViewById(R.id.reason_hostname_not_verified).setVisibility(View.GONE);
-            mView.findViewById(R.id.details_scroll).setVisibility(View.GONE);
-
-            /// refresh
-            if (mException.getCertPathValidatorException() != null) {
-                ((TextView)mView.findViewById(R.id.reason_cert_not_trusted)).setVisibility(View.VISIBLE);
-            }
-            
-            if (mException.getCertificateExpiredException() != null) {
-                ((TextView)mView.findViewById(R.id.reason_cert_expired)).setVisibility(View.VISIBLE);
-            }
-            
-            if (mException.getCertificateNotYetValidException() != null) {
-                ((TextView)mView.findViewById(R.id.reason_cert_not_yet_valid)).setVisibility(View.VISIBLE);
-            } 
-
-            if (mException.getSslPeerUnverifiedException() != null ) {
-                ((TextView)mView.findViewById(R.id.reason_hostname_not_verified)).setVisibility(View.VISIBLE);
-            }
-            
-            
-            showCertificateData(mException.getServerCertificate());
-        }
-        
-    }
-    
-    private void showCertificateData(X509Certificate cert) {
-
-        if (cert != null) {
-            showSubject(cert.getSubjectX500Principal());
-            showIssuer(cert.getIssuerX500Principal());
-            showValidity(cert.getNotBefore(), cert.getNotAfter());
-            showSignature(cert);
-            
-        } else {
-            // this should not happen
-            // TODO
-        }
-    }
-
-    private void showSignature(X509Certificate cert) {
-        TextView sigView = ((TextView)mView.findViewById(R.id.value_signature));
-        TextView algorithmView = ((TextView)mView.findViewById(R.id.value_signature_algorithm));
-        sigView.setText(getHex(cert.getSignature()));
-        algorithmView.setText(cert.getSigAlgName());
-    }
-    
-    public String getHex(final byte [] raw) {
-        if (raw == null) {
-           return null;
-        }
-        final StringBuilder hex = new StringBuilder(2 * raw.length);
-        for (final byte b : raw) {
-           final int hiVal = (b & 0xF0) >> 4;
-           final int loVal = b & 0x0F;
-           hex.append((char) ('0' + (hiVal + (hiVal / 10 * 7))));
-           hex.append((char) ('0' + (loVal + (loVal / 10 * 7))));
-        }
-        return hex.toString();
-     }    
-
-    private void showValidity(Date notBefore, Date notAfter) {
-        TextView fromView = ((TextView)mView.findViewById(R.id.value_validity_from));
-        TextView toView = ((TextView)mView.findViewById(R.id.value_validity_to));
-        fromView.setText(notBefore.toLocaleString());
-        toView.setText(notAfter.toLocaleString());
-    }
-
-    private void showSubject(X500Principal subject) {
-        Map<String, String> s = parsePrincipal(subject);
-        TextView cnView = ((TextView)mView.findViewById(R.id.value_subject_CN));
-        TextView oView = ((TextView)mView.findViewById(R.id.value_subject_O));
-        TextView ouView = ((TextView)mView.findViewById(R.id.value_subject_OU));
-        TextView cView = ((TextView)mView.findViewById(R.id.value_subject_C));
-        TextView stView = ((TextView)mView.findViewById(R.id.value_subject_ST));
-        TextView lView = ((TextView)mView.findViewById(R.id.value_subject_L));
-        
-        if (s.get("CN") != null) {
-            cnView.setText(s.get("CN"));
-            cnView.setVisibility(View.VISIBLE);
-        } else {
-            cnView.setVisibility(View.GONE);
-        }
-        if (s.get("O") != null) {
-            oView.setText(s.get("O"));
-            oView.setVisibility(View.VISIBLE);
-        } else {
-            oView.setVisibility(View.GONE);
-        }
-        if (s.get("OU") != null) {
-            ouView.setText(s.get("OU"));
-            ouView.setVisibility(View.VISIBLE);
-        } else {
-            ouView.setVisibility(View.GONE);
-        }
-        if (s.get("C") != null) {
-            cView.setText(s.get("C"));
-            cView.setVisibility(View.VISIBLE);
-        } else {
-            cView.setVisibility(View.GONE);
-        }
-        if (s.get("ST") != null) {
-            stView.setText(s.get("ST"));
-            stView.setVisibility(View.VISIBLE);
-        } else {
-            stView.setVisibility(View.GONE);
-        }
-        if (s.get("L") != null) {
-            lView.setText(s.get("L"));
-            lView.setVisibility(View.VISIBLE);
-        } else {
-            lView.setVisibility(View.GONE);
-        }
-    }
-    
-    private void showIssuer(X500Principal issuer) {
-        Map<String, String> s = parsePrincipal(issuer);
-        TextView cnView = ((TextView)mView.findViewById(R.id.value_issuer_CN));
-        TextView oView = ((TextView)mView.findViewById(R.id.value_issuer_O));
-        TextView ouView = ((TextView)mView.findViewById(R.id.value_issuer_OU));
-        TextView cView = ((TextView)mView.findViewById(R.id.value_issuer_C));
-        TextView stView = ((TextView)mView.findViewById(R.id.value_issuer_ST));
-        TextView lView = ((TextView)mView.findViewById(R.id.value_issuer_L));
-        
-        if (s.get("CN") != null) {
-            cnView.setText(s.get("CN"));
-            cnView.setVisibility(View.VISIBLE);
-        } else {
-            cnView.setVisibility(View.GONE);
-        }
-        if (s.get("O") != null) {
-            oView.setText(s.get("O"));
-            oView.setVisibility(View.VISIBLE);
-        } else {
-            oView.setVisibility(View.GONE);
-        }
-        if (s.get("OU") != null) {
-            ouView.setText(s.get("OU"));
-            ouView.setVisibility(View.VISIBLE);
-        } else {
-            ouView.setVisibility(View.GONE);
-        }
-        if (s.get("C") != null) {
-            cView.setText(s.get("C"));
-            cView.setVisibility(View.VISIBLE);
-        } else {
-            cView.setVisibility(View.GONE);
-        }
-        if (s.get("ST") != null) {
-            stView.setText(s.get("ST"));
-            stView.setVisibility(View.VISIBLE);
-        } else {
-            stView.setVisibility(View.GONE);
-        }
-        if (s.get("L") != null) {
-            lView.setText(s.get("L"));
-            lView.setVisibility(View.VISIBLE);
-        } else {
-            lView.setVisibility(View.GONE);
-        }
-    }
-    
-
-    private Map<String, String> parsePrincipal(X500Principal principal) {
-        Map<String, String> result = new HashMap<String, String>();
-        String toParse = principal.getName();
-        String[] pieces = toParse.split(",");
-        String[] tokens = {"CN", "O", "OU", "C", "ST", "L"}; 
-        for (int i=0; i < pieces.length ; i++) {
-            for (int j=0; j<tokens.length; j++) {
-                if (pieces[i].startsWith(tokens[j] + "=")) {
-                    result.put(tokens[j], pieces[i].substring(tokens[j].length()+1));
-                }
-            }
-        }
-        return result;
-    }
-
-    private void saveServerCert() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
-        if (mException.getServerCertificate() != null) {
-            // TODO make this asynchronously, it can take some time
-            OwnCloudClientUtils.addCertToKnownServersStore(mException.getServerCertificate(), getContext());
-        }
-    }
-
-    
-    public interface OnSslValidatorListener {
-        public void onSavedCertificate();
-        public void onFailedSavingCertificate();
-    }
-}
-
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/dialog/SsoWebView.java b/src/de/mobilcom/debitel/cloud/android/ui/dialog/SsoWebView.java
deleted file mode 100644 (file)
index 2186bc4..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.dialog;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.webkit.WebView;
-
-public class SsoWebView extends WebView {
-    
-    public SsoWebView(Context context) {
-        super(context);
-    }
-    
-    public SsoWebView(Context context, AttributeSet attr) {
-        super(context, attr);
-    }
-    
-    @Override
-    public boolean onCheckIsTextEditor () {
-        return false;
-    }
-    
-}
-
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/fragment/AuthenticatorAccountDetailsFragment.java b/src/de/mobilcom/debitel/cloud/android/ui/fragment/AuthenticatorAccountDetailsFragment.java
deleted file mode 100644 (file)
index d01e6e6..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.fragment;
-
-import com.actionbarsherlock.app.SherlockFragment;
-
-public class AuthenticatorAccountDetailsFragment extends SherlockFragment {
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/fragment/AuthenticatorGetStartedFragment.java b/src/de/mobilcom/debitel/cloud/android/ui/fragment/AuthenticatorGetStartedFragment.java
deleted file mode 100644 (file)
index 8f38fd4..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.fragment;
-
-import com.actionbarsherlock.app.SherlockFragment;
-
-public class AuthenticatorGetStartedFragment extends SherlockFragment {
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/fragment/ConfirmationDialogFragment.java b/src/de/mobilcom/debitel/cloud/android/ui/fragment/ConfirmationDialogFragment.java
deleted file mode 100644 (file)
index 641ba1d..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.fragment;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-
-import com.actionbarsherlock.app.SherlockDialogFragment;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-
-public class ConfirmationDialogFragment extends SherlockDialogFragment {
-
-    public final static String ARG_CONF_RESOURCE_ID = "resource_id";
-    public final static String ARG_CONF_ARGUMENTS = "string_array";
-    
-    public final static String ARG_POSITIVE_BTN_RES = "positive_btn_res";
-    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;
-    
-    /**
-     * Public factory method to create new ConfirmationDialogFragment instances.
-     * 
-     * @param string_id         Resource id for a message to show in the dialog.
-     * @param arguments         Arguments to complete the message, if it's a format string.
-     * @param posBtn            Resource id for the text of the positive button.
-     * @param neuBtn            Resource id for the text of the neutral button.
-     * @param negBtn            Resource id for the text of the negative button.
-     * @return                  Dialog ready to show.
-     */
-    public static ConfirmationDialogFragment newInstance(int string_id, String[] arguments, int posBtn, int neuBtn, int negBtn) {
-        ConfirmationDialogFragment frag = new ConfirmationDialogFragment();
-        Bundle args = new Bundle();
-        args.putInt(ARG_CONF_RESOURCE_ID, string_id);
-        args.putStringArray(ARG_CONF_ARGUMENTS, arguments);
-        args.putInt(ARG_POSITIVE_BTN_RES, posBtn);
-        args.putInt(ARG_NEUTRAL_BTN_RES, neuBtn);
-        args.putInt(ARG_NEGATIVE_BTN_RES, negBtn);
-        frag.setArguments(args);
-        return frag;
-    }
-    
-    public void setOnConfirmationListener(ConfirmationDialogFragmentListener listener) {
-        mListener = listener;
-    }
-
-    @Override
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        Object[] confirmationTarget = getArguments().getStringArray(ARG_CONF_ARGUMENTS);
-        int resourceId = getArguments().getInt(ARG_CONF_RESOURCE_ID, -1);
-        int posBtn = getArguments().getInt(ARG_POSITIVE_BTN_RES, -1);
-        int neuBtn = getArguments().getInt(ARG_NEUTRAL_BTN_RES, -1);
-        int negBtn = getArguments().getInt(ARG_NEGATIVE_BTN_RES, -1);
-        
-        if (confirmationTarget == null || resourceId == -1) {
-            Log_OC.wtf(getTag(), "Calling confirmation dialog without resource or arguments");
-            return null;
-        }
-
-        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
-            .setIcon(android.R.drawable.ic_dialog_alert)
-            .setMessage(String.format(getString(resourceId), confirmationTarget))
-            .setTitle(android.R.string.dialog_alert_title);
-        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
-            builder.setIconAttribute(android.R.attr.alertDialogIcon);
-        }
-        
-        if (posBtn != -1)
-            builder.setPositiveButton(posBtn,
-                    new DialogInterface.OnClickListener() {
-                        public void onClick(DialogInterface dialog, int whichButton) {
-                            mListener.onConfirmation(getTag()); 
-                            dialog.dismiss();
-                        }
-                    });
-        if (neuBtn != -1)
-            builder.setNeutralButton(neuBtn,
-                    new DialogInterface.OnClickListener() {
-                        public void onClick(DialogInterface dialog, int whichButton) {
-                            mListener.onNeutral(getTag()); 
-                            dialog.dismiss();
-                        }
-                    });
-        if (negBtn != -1)
-            builder.setNegativeButton(negBtn,
-                    new DialogInterface.OnClickListener() {
-                        @Override
-                        public void onClick(DialogInterface dialog, int which) {
-                            mListener.onCancel(getTag());
-                            dialog.dismiss();
-                        }
-                    });
-      return builder.create();
-    }
-    
-    
-    public interface ConfirmationDialogFragmentListener {
-        public void onConfirmation(String callerTag);
-        public void onNeutral(String callerTag);
-        public void onCancel(String callerTag);
-    }
-    
-}
-
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/fragment/ExtendedListFragment.java b/src/de/mobilcom/debitel/cloud/android/ui/fragment/ExtendedListFragment.java
deleted file mode 100644 (file)
index b152b25..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.fragment;
-
-import com.actionbarsherlock.app.SherlockFragment;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.ui.ExtendedListView;
-
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.ListAdapter;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.ListView;
-
-/**
- *  TODO extending SherlockListFragment instead of SherlockFragment 
- */
-public class ExtendedListFragment extends SherlockFragment implements OnItemClickListener {
-    
-    private static final String TAG = ExtendedListFragment.class.getSimpleName();
-
-    private static final String KEY_SAVED_LIST_POSITION = "SAVED_LIST_POSITION"; 
-
-    protected ExtendedListView mList;
-    
-    public void setListAdapter(ListAdapter listAdapter) {
-        mList.setAdapter(listAdapter);
-        mList.invalidate();
-    }
-
-    public ListView getListView() {
-        return mList;
-    }
-    
-    
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        Log_OC.e(TAG, "onCreateView");
-        //mList = new ExtendedListView(getActivity());
-        View v = inflater.inflate(R.layout.list_fragment, null);
-        mList = (ExtendedListView)(v.findViewById(R.id.list_root));
-        mList.setOnItemClickListener(this);
-        //mList.setEmptyView(v.findViewById(R.id.empty_list_view));     // looks like it's not a cool idea 
-        mList.setDivider(getResources().getDrawable(R.drawable.uploader_list_separator));
-        mList.setDividerHeight(1);
-
-        if (savedInstanceState != null) {
-            int referencePosition = savedInstanceState.getInt(KEY_SAVED_LIST_POSITION);
-            setReferencePosition(referencePosition);
-        }
-        
-        return v;
-    }
-
-    
-    @Override
-    public void onSaveInstanceState(Bundle savedInstanceState) {
-        super.onSaveInstanceState(savedInstanceState);
-        Log_OC.e(TAG, "onSaveInstanceState()");
-        savedInstanceState.putInt(KEY_SAVED_LIST_POSITION, getReferencePosition());
-    }
-
-    
-    /**
-     * Calculates the position of the item that will be used as a reference to reposition the visible items in the list when
-     * the device is turned to other position. 
-     * 
-     * THe current policy is take as a reference the visible item in the center of the screen.  
-     * 
-     * @return      The position in the list of the visible item in the center of the screen.
-     */
-    protected int getReferencePosition() {
-        if (mList != null) {
-            return (mList.getFirstVisiblePosition() + mList.getLastVisiblePosition()) / 2;
-        } else {
-            return 0;
-        }
-    }
-
-    
-    /**
-     * Sets the visible part of the list from the reference position.
-     * 
-     * @param   position    Reference position previously returned by {@link LocalFileListFragment#getReferencePosition()}
-     */
-    protected void setReferencePosition(int position) {
-        if (mList != null) {
-            mList.setAndCenterSelection(position);
-        }
-    }
-
-    @Override
-    public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
-        // to be @overriden  
-    }
-
-    
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/fragment/FileDetailFragment.java b/src/de/mobilcom/debitel/cloud/android/ui/fragment/FileDetailFragment.java
deleted file mode 100644 (file)
index bb0e4b9..0000000
+++ /dev/null
@@ -1,930 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.fragment;
-
-import java.io.File;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-
-import android.accounts.Account;
-import android.app.Activity;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.os.Handler;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.CheckBox;
-import android.widget.ImageView;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.actionbarsherlock.view.Menu;
-import com.actionbarsherlock.view.MenuInflater;
-import com.actionbarsherlock.view.MenuItem;
-
-import de.mobilcom.debitel.cloud.android.DisplayUtils;
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.files.services.FileObserverService;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader;
-import de.mobilcom.debitel.cloud.android.files.services.FileDownloader.FileDownloaderBinder;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader.FileUploaderBinder;
-import de.mobilcom.debitel.cloud.android.operations.OnRemoteOperationListener;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult;
-import de.mobilcom.debitel.cloud.android.operations.RemoveFileOperation;
-import de.mobilcom.debitel.cloud.android.operations.RenameFileOperation;
-import de.mobilcom.debitel.cloud.android.operations.SynchronizeFileOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-import de.mobilcom.debitel.cloud.android.ui.activity.ConflictsResolveActivity;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileActivity;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileDisplayActivity;
-import de.mobilcom.debitel.cloud.android.ui.dialog.EditNameDialog;
-import de.mobilcom.debitel.cloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
-import de.mobilcom.debitel.cloud.android.ui.preview.PreviewImageFragment;
-
-import eu.alefzero.webdav.OnDatatransferProgressListener;
-
-/**
- * This Fragment is used to display the details about a file.
- * 
- * @author Bartek Przybylski
- * @author David A. Velasco
- */
-public class FileDetailFragment extends FileFragment implements
-        OnClickListener, 
-        ConfirmationDialogFragment.ConfirmationDialogFragmentListener, OnRemoteOperationListener, EditNameDialogListener {
-
-    private FileFragment.ContainerActivity mContainerActivity;
-    
-    private int mLayout;
-    private View mView;
-    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_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() {
-        super();
-        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) {
-        super(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();
-        setHasOptionsMenu(true);
-    }
-    
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        //super.onCreateView(inflater, container, savedInstanceState);
-        
-        if (savedInstanceState != null) {
-            setFile((OCFile)savedInstanceState.getParcelable(FileActivity.EXTRA_FILE));
-            mAccount = savedInstanceState.getParcelable(FileActivity.EXTRA_ACCOUNT);
-        }
-        
-        if(getFile() != null && mAccount != null) {
-            mLayout = R.layout.file_details_fragment;
-        }
-        
-        View view = null;
-        //view = inflater.inflate(mLayout, container, false);
-        view = inflater.inflate(mLayout, null);
-        mView = view;
-        
-        if (mLayout == R.layout.file_details_fragment) {
-            mView.findViewById(R.id.fdKeepInSync).setOnClickListener(this);
-            ProgressBar progressBar = (ProgressBar)mView.findViewById(R.id.fdProgressBar);
-            mProgressListener = new ProgressListener(progressBar);
-            mView.findViewById(R.id.fdCancelBtn).setOnClickListener(this);
-        }
-        
-        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(FileActivity.EXTRA_FILE, getFile());
-        outState.putParcelable(FileActivity.EXTRA_ACCOUNT, mAccount);
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        listenForTransferProgress();
-    }
-    
-    @Override
-    public void onResume() {
-        super.onResume();
-        mUploadFinishReceiver = new UploadFinishReceiver();
-        FileUploader fileUploader = new FileUploader();
-        IntentFilter filter = new IntentFilter(fileUploader.getUploadFinishMessage());
-        getActivity().registerReceiver(mUploadFinishReceiver, filter);
-
-    }
-
-
-    @Override
-    public void onPause() {
-        super.onPause();
-        if (mUploadFinishReceiver != null) {
-            getActivity().unregisterReceiver(mUploadFinishReceiver);
-            mUploadFinishReceiver = null;
-        }
-    }
-
-    
-    @Override
-    public void onStop() {
-        super.onStop();
-        leaveTransferProgress();
-    }
-
-    
-    @Override
-    public View getView() {
-        return super.getView() == null ? mView : super.getView();
-    }
-
-    
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        super.onCreateOptionsMenu(menu, inflater);
-        inflater.inflate(R.menu.file_actions_menu, menu);
-        MenuItem item = menu.findItem(R.id.action_see_details);
-        if (item != null) {
-            item.setVisible(false);
-            item.setEnabled(false);
-        }
-    }
-
-    
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onPrepareOptionsMenu (Menu menu) {
-        super.onPrepareOptionsMenu(menu);
-        
-        List<Integer> toHide = new ArrayList<Integer>();
-        List<Integer> toShow = new ArrayList<Integer>();
-        OCFile file = getFile();
-        
-        FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
-        boolean downloading = downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file);
-        FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
-        boolean uploading = uploaderBinder != null && uploaderBinder.isUploading(mAccount, getFile());
-        
-        if (downloading || uploading) {
-            toHide.add(R.id.action_download_file);
-            toHide.add(R.id.action_rename_file);
-            toHide.add(R.id.action_remove_file);
-            toHide.add(R.id.action_open_file_with);
-            if (!downloading) {
-                toHide.add(R.id.action_cancel_download);
-                toShow.add(R.id.action_cancel_upload);
-            } else {
-                toHide.add(R.id.action_cancel_upload);
-                toShow.add(R.id.action_cancel_download);
-            }
-
-        } else if (file != null && file.isDown()) {
-            toHide.add(R.id.action_download_file);
-            toHide.add(R.id.action_cancel_download);
-            toHide.add(R.id.action_cancel_upload);
-            
-            toShow.add(R.id.action_rename_file);
-            toShow.add(R.id.action_remove_file);
-            toShow.add(R.id.action_open_file_with);
-            toShow.add(R.id.action_sync_file);
-            
-        } else if (file != null) {
-            toHide.add(R.id.action_open_file_with);
-            toHide.add(R.id.action_cancel_download);
-            toHide.add(R.id.action_cancel_upload);
-            toHide.add(R.id.action_sync_file);
-            
-            toShow.add(R.id.action_rename_file);
-            toShow.add(R.id.action_remove_file);
-            toShow.add(R.id.action_download_file);
-            
-        } else {
-            toHide.add(R.id.action_open_file_with);
-            toHide.add(R.id.action_cancel_download);
-            toHide.add(R.id.action_cancel_upload);
-            toHide.add(R.id.action_sync_file);
-            toHide.add(R.id.action_download_file);
-            toHide.add(R.id.action_rename_file);
-            toHide.add(R.id.action_remove_file);
-            
-        }
-
-        MenuItem item = null;
-        for (int i : toHide) {
-            item = menu.findItem(i);
-            if (item != null) {
-                item.setVisible(false);
-                item.setEnabled(false);
-            }
-        }
-        for (int i : toShow) {
-            item = menu.findItem(i);
-            if (item != null) {
-                item.setVisible(true);
-                item.setEnabled(true);
-            }
-        }
-    }
-
-    
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case R.id.action_open_file_with: {
-                mContainerActivity.openFile(getFile());
-                return true;
-            }
-            case R.id.action_remove_file: {
-                removeFile();
-                return true;
-            }
-            case R.id.action_rename_file: {
-                renameFile();
-                return true;
-            }
-            case R.id.action_download_file: 
-            case R.id.action_cancel_download:
-            case R.id.action_cancel_upload:
-            case R.id.action_sync_file: {
-                synchronizeFile();
-                return true;
-            }
-            default:
-                return false;
-        }
-    }
-    
-    @Override
-    public void onClick(View v) {
-        switch (v.getId()) {
-            case R.id.fdKeepInSync: {
-                toggleKeepInSync();
-                break;
-            }
-            case R.id.fdCancelBtn: {
-                synchronizeFile();
-                break;
-            }
-            default:
-                Log_OC.e(TAG, "Incorrect view clicked!");
-        }
-    }
-    
-    
-    private void toggleKeepInSync() {
-        CheckBox cb = (CheckBox) getView().findViewById(R.id.fdKeepInSync);
-        OCFile file = getFile();
-        file.setKeepInSync(cb.isChecked());
-        mStorageManager.saveFile(file);
-        
-        /// 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, file);
-        intent.putExtra(FileObserverService.KEY_CMD_ARG_ACCOUNT, mAccount);
-        getActivity().startService(intent);
-        
-        if (file.keepInSync()) {
-            synchronizeFile();   // force an immediate synchronization
-        }
-    }
-
-
-    private void removeFile() {
-        OCFile file = getFile();
-        ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
-                R.string.confirmation_remove_alert,
-                new String[]{file.getFileName()},
-                file.isDown() ? R.string.confirmation_remove_remote_and_local : R.string.confirmation_remove_remote,
-                file.isDown() ? R.string.confirmation_remove_local : -1,
-                R.string.common_cancel);
-        confDialog.setOnConfirmationListener(this);
-        confDialog.show(getFragmentManager(), FTAG_CONFIRMATION);
-    }
-
-
-    private void renameFile() {
-        OCFile file = getFile();
-        String fileName = file.getFileName();
-        int extensionStart = file.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");
-    }
-
-    private void synchronizeFile() {
-        OCFile file = getFile();
-        FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
-        FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
-        if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) {
-            downloaderBinder.cancel(mAccount, file);
-            if (file.isDown()) {
-                setButtonsForDown();
-            } else {
-                setButtonsForRemote();
-            }
-
-        } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file)) {
-            uploaderBinder.cancel(mAccount, file);
-            if (!file.fileExists()) {
-                // TODO make something better
-                ((FileDisplayActivity)getActivity()).cleanSecondFragment();
-                
-            } else if (file.isDown()) {
-                setButtonsForDown();
-            } else {
-                setButtonsForRemote();
-            }
-            
-        } else {
-            mLastRemoteOperation = new SynchronizeFileOperation(file, null, mStorageManager, mAccount, true, false, getActivity());
-            mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
-            
-            // update ui 
-            ((FileDisplayActivity) getActivity()).showLoadingDialog();
-            
-        }
-    }
-
-    @Override
-    public void onConfirmation(String callerTag) {
-        OCFile file = getFile();
-        if (callerTag.equals(FTAG_CONFIRMATION)) {
-            if (mStorageManager.getFileById(file.getFileId()) != null) {
-                mLastRemoteOperation = new RemoveFileOperation( file, 
-                                                                true, 
-                                                                mStorageManager);
-                mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
-                                
-                ((FileDisplayActivity) getActivity()).showLoadingDialog();
-            }
-        }
-    }
-    
-    @Override
-    public void onNeutral(String callerTag) {
-        File f = null;
-        OCFile file = getFile();
-        if (file.isDown() && (f = new File(file.getStoragePath())).exists()) {
-            f.delete();
-            file.setStoragePath(null);
-            mStorageManager.saveFile(file);
-            updateFileDetails(file, 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 || getFile() == null || mAccount == null);
-    }
-
-    
-    /**
-     * Use this method to signal this Activity that it shall update its view.
-     * 
-     * @param file : An {@link OCFile}
-     */
-    public void updateFileDetails(OCFile file, Account ocAccount) {
-        setFile(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) {
-                setFile(mStorageManager.getFileByPath(getFile().getRemotePath()));
-            }
-            OCFile file = getFile();
-            
-            // set file details
-            setFilename(file.getFileName());
-            setFiletype(file.getMimetype());
-            setFilesize(file.getFileLength());
-            if(ocVersionSupportsTimeCreated()){
-                setTimeCreated(file.getCreationTimestamp());
-            }
-           
-            setTimeModified(file.getModificationTimestamp());
-            
-            CheckBox cb = (CheckBox)getView().findViewById(R.id.fdKeepInSync);
-            cb.setChecked(file.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, file)) || (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file))) {
-                setButtonsForTransferring();
-                
-            } else if (file.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 (getFile() != null && mAccount != null && mLayout == R.layout.file_details_fragment);        
-    }
-
-
-    /**
-     * Updates the filename in view
-     * @param filename to set
-     */
-    private void setFilename(String filename) {
-        TextView tv = (TextView) getView().findViewById(R.id.fdFilename);
-        if (tv != null)
-            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()) {
-            // let's protect the user from himself ;)
-            getView().findViewById(R.id.fdKeepInSync).setEnabled(false);
-            
-            // show the progress bar for the transfer
-            getView().findViewById(R.id.fdProgressBlock).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, getFile())) {
-                progressText.setText(R.string.downloader_download_in_progress_ticker);
-            } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, getFile())) {
-                progressText.setText(R.string.uploader_upload_in_progress_ticker);
-            }
-        }
-    }
-
-    /**
-     * Enables or disables buttons for a file locally available 
-     */
-    private void setButtonsForDown() {
-        if (!isEmpty()) {
-            getView().findViewById(R.id.fdKeepInSync).setEnabled(true);
-            
-            // hides the progress bar
-            getView().findViewById(R.id.fdProgressBlock).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()) {
-            getView().findViewById(R.id.fdKeepInSync).setEnabled(true);
-            
-            // hides the progress bar
-            getView().findViewById(R.id.fdProgressBlock).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 = getFile().getRemotePath().equals(intent.getStringExtra(FileUploader.EXTRA_OLD_REMOTE_PATH));
-                if (getFile().getRemotePath().equals(uploadRemotePath) ||
-                    renamedInUpload) {
-                    if (uploadWasFine) {
-                        setFile(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
-                   
-                    // Force the preview if the file is an image
-                    if (uploadWasFine && PreviewImageFragment.canBePreviewed(getFile())) {
-                        ((FileDisplayActivity) mContainerActivity).startImagePreview(getFile());
-                    } 
-                }
-            }
-        }
-    }
-    
-
-    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( getFile(), 
-                                                            mAccount, 
-                                                            newFilename, 
-                                                            new FileDataStorageManager(mAccount, getActivity().getContentResolver()));
-            mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
-            ((FileDisplayActivity) getActivity()).showLoadingDialog();
-        }
-    }
-    
-    
-    /**
-     * {@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) {
-        ((FileDisplayActivity) getActivity()).dismissLoadingDialog();
-        if (result.isSuccess()) {
-            Toast msg = Toast.makeText(getActivity().getApplicationContext(), R.string.remove_success_msg, Toast.LENGTH_LONG);
-            msg.show();
-            ((FileDisplayActivity)getActivity()).cleanSecondFragment();
-
-        } 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) {
-        ((FileDisplayActivity) getActivity()).dismissLoadingDialog();
-        
-        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) {
-        ((FileDisplayActivity) getActivity()).dismissLoadingDialog();
-        OCFile file = getFile();
-        if (!result.isSuccess()) {
-            if (result.getCode() == ResultCode.SYNC_CONFLICT) {
-                Intent i = new Intent(getActivity(), ConflictsResolveActivity.class);
-                i.putExtra(ConflictsResolveActivity.EXTRA_FILE, file);
-                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 (file.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 (file.isDown()) {
-                    setButtonsForDown();
-                    
-                } else {
-                    setButtonsForRemote();
-                }
-            }
-        }
-    }
-    
-
-    public void listenForTransferProgress() {
-        if (mProgressListener != null) {
-            if (mContainerActivity.getFileDownloaderBinder() != null) {
-                mContainerActivity.getFileDownloaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, getFile());
-            }
-            if (mContainerActivity.getFileUploaderBinder() != null) {
-                mContainerActivity.getFileUploaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, getFile());
-            }
-        }
-    }
-    
-    
-    public void leaveTransferProgress() {
-        if (mProgressListener != null) {
-            if (mContainerActivity.getFileDownloaderBinder() != null) {
-                mContainerActivity.getFileDownloaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, getFile());
-            }
-            if (mContainerActivity.getFileUploaderBinder() != null) {
-                mContainerActivity.getFileUploaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, getFile());
-            }
-        }
-    }
-
-
-    
-    /**
-     * 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;
-        }
-
-    };
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/fragment/FileFragment.java b/src/de/mobilcom/debitel/cloud/android/ui/fragment/FileFragment.java
deleted file mode 100644 (file)
index df1a0d5..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.fragment;
-
-import android.support.v4.app.Fragment;
-
-import com.actionbarsherlock.app.SherlockFragment;
-
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.files.FileHandler;
-import de.mobilcom.debitel.cloud.android.ui.activity.TransferServiceGetter;
-
-/**
- * Common methods for {@link Fragment}s containing {@link OCFile}s
- * 
- * @author David A. Velasco
- *
- */
-public class FileFragment extends SherlockFragment {
-    
-    private OCFile mFile;
-
-
-    /**
-     * Creates an empty fragment.
-     * 
-     * It's necessary to keep a public constructor without parameters; the system uses it when tries to reinstantiate a fragment automatically. 
-     */
-    public FileFragment() {
-        mFile = null;
-    }
-    
-    /**
-     * Creates an instance for a given {@OCFile}.
-     * 
-     * @param file
-     */
-    public FileFragment(OCFile file) {
-        mFile = file;
-    }
-
-    /**
-     * Getter for the hold {@link OCFile}
-     * 
-     * @return The {@link OCFile} hold
-     */
-    public OCFile getFile() {
-        return mFile;
-    }
-    
-    
-    protected void setFile(OCFile file) {
-        mFile = file;
-    }
-
-    /**
-     * Interface to implement by any Activity that includes some instance of FileFragment
-     * 
-     * @author David A. Velasco
-     */
-    public interface ContainerActivity extends TransferServiceGetter, FileHandler {
-
-        /**
-         * 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 showDetails(OCFile file);
-        
-        
-    }
-    
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/fragment/LandingPageFragment.java b/src/de/mobilcom/debitel/cloud/android/ui/fragment/LandingPageFragment.java
deleted file mode 100644 (file)
index 45667ea..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/* 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 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
-package de.mobilcom.debitel.cloud.android.ui.fragment;\r
-\r
-import com.actionbarsherlock.app.SherlockFragment;\r
-\r
-import android.os.Bundle;\r
-import android.view.LayoutInflater;\r
-import android.view.View;\r
-import android.view.ViewGroup;\r
-import android.widget.ListView;\r
-import de.mobilcom.debitel.cloud.android.R;\r
-import de.mobilcom.debitel.cloud.android.ui.activity.LandingActivity;\r
-import de.mobilcom.debitel.cloud.android.ui.adapter.LandingScreenAdapter;\r
-\r
-/**\r
- * Used on the Landing page to display what Components of the ownCloud there\r
- * are. Like Files, Music, Contacts, etc.\r
- * \r
- * @author Lennart Rosam\r
- * \r
- */\r
-public class LandingPageFragment extends SherlockFragment {\r
-\r
-    @Override\r
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,\r
-            Bundle savedInstanceState) {\r
-        View root = inflater.inflate(R.layout.landing_page_fragment, container);\r
-        return root;\r
-    }\r
-\r
-    @Override\r
-    public void onActivityCreated(Bundle savedInstanceState) {\r
-        super.onActivityCreated(savedInstanceState);\r
-\r
-        ListView landingScreenItems = (ListView) getView().findViewById(\r
-                R.id.homeScreenList);\r
-        landingScreenItems.setAdapter(new LandingScreenAdapter(getActivity()));\r
-        landingScreenItems\r
-                .setOnItemClickListener((LandingActivity) getActivity());\r
-    }\r
-\r
-}\r
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/fragment/LocalFileListFragment.java b/src/de/mobilcom/debitel/cloud/android/ui/fragment/LocalFileListFragment.java
deleted file mode 100644 (file)
index aa1e34a..0000000
+++ /dev/null
@@ -1,250 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.fragment;
-
-import java.io.File;
-
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.os.Environment;
-import android.util.SparseBooleanArray;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.ImageView;
-import android.widget.ListView;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.ui.adapter.LocalFileListAdapter;
-
-/**
- * A Fragment that lists all files and folders in a given LOCAL path.
- * 
- * @author David A. Velasco
- * 
- */
-public class LocalFileListFragment extends ExtendedListFragment {
-    private static final String TAG = "LocalFileListFragment";
-    
-    /** Reference to the Activity which this fragment is attached to. For callbacks */
-    private LocalFileListFragment.ContainerActivity mContainerActivity;
-    
-    /** Directory to show */
-    private File mDirectory = null;
-    
-    /** Adapter to connect the data from the directory with the View object */
-    private LocalFileListAdapter mAdapter = null;
-
-    
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onAttach(Activity activity) {
-        super.onAttach(activity);
-        try {
-            mContainerActivity = (ContainerActivity) activity;
-        } catch (ClassCastException e) {
-            throw new ClassCastException(activity.toString() + " must implement " + LocalFileListFragment.ContainerActivity.class.getSimpleName());
-        }
-    }
-    
-    
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        Log_OC.i(TAG, "onCreateView() start");
-        View v = super.onCreateView(inflater, container, savedInstanceState);
-        getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
-        Log_OC.i(TAG, "onCreateView() end");
-        return v;
-    }    
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        Log_OC.i(TAG, "onActivityCreated() start");
-        
-        super.onCreate(savedInstanceState);
-        mAdapter = new LocalFileListAdapter(mContainerActivity.getInitialDirectory(), getActivity());
-        setListAdapter(mAdapter);
-        
-        Log_OC.i(TAG, "onActivityCreated() stop");
-    }
-    
-    
-    /**
-     * Checks the file clicked over. Browses inside if it is a directory. Notifies the container activity in any case.
-     */
-    @Override
-    public void onItemClick(AdapterView<?> l, View v, int position, long id) {
-        File file = (File) mAdapter.getItem(position); 
-        if (file != null) {
-            /// Click on a directory
-            if (file.isDirectory()) {
-                // just local updates
-                listDirectory(file);
-                // notify the click to container Activity
-                mContainerActivity.onDirectoryClick(file);
-            
-            } else {    /// Click on a file
-                ImageView checkBoxV = (ImageView) v.findViewById(R.id.custom_checkbox);
-                if (checkBoxV != null) {
-                    if (getListView().isItemChecked(position)) {
-                        checkBoxV.setImageResource(android.R.drawable.checkbox_on_background);
-                    } else {
-                        checkBoxV.setImageResource(android.R.drawable.checkbox_off_background);
-                    }
-                }
-                // notify the change to the container Activity
-                mContainerActivity.onFileClick(file);
-            }
-            
-        } else {
-            Log_OC.w(TAG, "Null object in ListAdapter!!");
-        }
-    }
-
-    
-    /**
-     * Call this, when the user presses the up button
-     */
-    public void onNavigateUp() {
-        File parentDir = null;
-        if(mDirectory != null) {
-            parentDir = mDirectory.getParentFile();  // can be null
-        }
-        listDirectory(parentDir);
-    }
-
-    
-    /**
-     * Use this to query the {@link File} object for the directory
-     * that is currently being displayed by this fragment
-     * 
-     * @return File     The currently displayed directory
-     */
-    public File getCurrentDirectory(){
-        return mDirectory;
-    }
-    
-    
-    /**
-     * Calls {@link LocalFileListFragment#listDirectory(File)} with a null parameter
-     * to refresh the current directory.
-     */
-    public void listDirectory(){
-        listDirectory(null);
-    }
-    
-    
-    /**
-     * Lists the given directory on the view. When the input parameter is null,
-     * it will either refresh the last known directory. list the root
-     * if there never was a directory.
-     * 
-     * @param directory     Directory to be listed
-     */
-    public void listDirectory(File directory) {
-        
-        // Check input parameters for null
-        if(directory == null) {
-            if(mDirectory != null){
-                directory = mDirectory;
-            } else {
-                directory = Environment.getExternalStorageDirectory();  // TODO be careful with the state of the storage; could not be available
-                if (directory == null) return; // no files to show
-            }
-        }
-        
-        
-        // if that's not a directory -> List its parent
-        if(!directory.isDirectory()){
-            Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
-            directory = directory.getParentFile();
-        }
-
-        mList.clearChoices();   // by now, only files in the same directory will be kept as selected
-        mAdapter.swapDirectory(directory);
-        if (mDirectory == null || !mDirectory.equals(directory)) {
-            mList.setSelectionFromTop(0, 0);
-        }
-        mDirectory = directory;
-    }
-    
-
-    /**
-     * Returns the fule paths to the files checked by the user
-     * 
-     * @return      File paths to the files checked by the user.
-     */
-    public String[] getCheckedFilePaths() {
-        String [] result = null;
-        SparseBooleanArray positions = mList.getCheckedItemPositions();
-        if (positions.size() > 0) {
-            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();
-            }
-        }
-        return result;
-    }
-
-    
-    /**
-     * Interface to implement by any Activity that includes some instance of LocalFileListFragment
-     * 
-     * @author David A. Velasco
-     */
-    public interface ContainerActivity {
-
-        /**
-         * Callback method invoked when a directory is clicked by the user on the files list
-         *  
-         * @param file
-         */
-        public void onDirectoryClick(File directory);
-        
-        /**
-         * Callback method invoked when a file (non directory) is clicked by the user on the files list
-         *  
-         * @param file
-         */
-        public void onFileClick(File file);
-        
-        
-        /**
-         * Callback method invoked when the parent activity is fully created to get the directory to list firstly.
-         * 
-         * @return  Directory to list firstly. Can be NULL.
-         */
-        public File getInitialDirectory();
-
-    }
-
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/fragment/OCFileListFragment.java b/src/de/mobilcom/debitel/cloud/android/ui/fragment/OCFileListFragment.java
deleted file mode 100644 (file)
index 6b05c2b..0000000
+++ /dev/null
@@ -1,477 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.fragment;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.files.FileHandler;
-import de.mobilcom.debitel.cloud.android.files.services.FileDownloader.FileDownloaderBinder;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader.FileUploaderBinder;
-import de.mobilcom.debitel.cloud.android.operations.OnRemoteOperationListener;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoveFileOperation;
-import de.mobilcom.debitel.cloud.android.operations.RenameFileOperation;
-import de.mobilcom.debitel.cloud.android.operations.SynchronizeFileOperation;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileDisplayActivity;
-import de.mobilcom.debitel.cloud.android.ui.activity.TransferServiceGetter;
-import de.mobilcom.debitel.cloud.android.ui.adapter.FileListListAdapter;
-import de.mobilcom.debitel.cloud.android.ui.dialog.EditNameDialog;
-import de.mobilcom.debitel.cloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
-import de.mobilcom.debitel.cloud.android.ui.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
-import de.mobilcom.debitel.cloud.android.ui.preview.PreviewImageFragment;
-import de.mobilcom.debitel.cloud.android.ui.preview.PreviewMediaFragment;
-
-import android.accounts.Account;
-import android.app.Activity;
-import android.os.Bundle;
-import android.os.Handler;
-import android.view.ContextMenu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.AdapterView.AdapterContextMenuInfo;
-
-/**
- * A Fragment that lists all files and folders in a given path.
- * 
- * @author Bartek Przybylski
- * 
- */
-public class OCFileListFragment extends ExtendedListFragment implements EditNameDialogListener, ConfirmationDialogFragmentListener {
-    
-    private static final String TAG = OCFileListFragment.class.getSimpleName();
-
-    private static final String MY_PACKAGE = OCFileListFragment.class.getPackage() != null ? OCFileListFragment.class.getPackage().getName() : "de.mobilcom.debitel.cloud.android.ui.fragment";
-    private static final String EXTRA_FILE = MY_PACKAGE + ".extra.FILE";
-    
-    private OCFileListFragment.ContainerActivity mContainerActivity;
-    
-    private OCFile mFile = null;
-    private FileListListAdapter mAdapter;
-    
-    private Handler mHandler;
-    private OCFile mTargetFile;
-    
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onAttach(Activity activity) {
-        super.onAttach(activity);
-        Log_OC.e(TAG, "onAttach");
-        try {
-            mContainerActivity = (ContainerActivity) activity;
-        } catch (ClassCastException e) {
-            throw new ClassCastException(activity.toString() + " must implement " + OCFileListFragment.ContainerActivity.class.getSimpleName());
-        }
-    }
-    
-    
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
-        Log_OC.e(TAG, "onActivityCreated() start");
-        mAdapter = new FileListListAdapter(getActivity(), mContainerActivity);
-        if (savedInstanceState != null) {
-            mFile = savedInstanceState.getParcelable(EXTRA_FILE);
-        }
-        setListAdapter(mAdapter);
-        
-        registerForContextMenu(getListView());
-        getListView().setOnCreateContextMenuListener(this);        
-        
-        mHandler = new Handler();
-
-    }
-    
-    /**
-     * Saves the current listed folder.
-     */
-    @Override
-    public void onSaveInstanceState (Bundle outState) {
-        super.onSaveInstanceState(outState);
-        outState.putParcelable(EXTRA_FILE, mFile);
-    }
-
-
-    /**
-     * Call this, when the user presses the up button
-     */
-    public void onBrowseUp() {
-        OCFile parentDir = null;
-        
-        if(mFile != null){
-            DataStorageManager storageManager = mContainerActivity.getStorageManager();
-            parentDir = storageManager.getFileById(mFile.getParentId());
-            mFile = parentDir;
-        }
-        listDirectory(parentDir);
-    }
-    
-    @Override
-    public void onItemClick(AdapterView<?> l, View v, int position, long id) {
-        OCFile file = (OCFile) mAdapter.getItem(position);
-        if (file != null) {
-            if (file.isDirectory()) { 
-                // update state and view of this fragment
-                listDirectory(file);
-                // then, notify parent activity to let it update its state and view, and other fragments
-                mContainerActivity.onBrowsedDownTo(file);
-                
-            } else { /// Click on a file
-                if (PreviewImageFragment.canBePreviewed(file)) {
-                    // preview image - it handles the download, if needed
-                    mContainerActivity.startImagePreview(file);
-                    
-                } else if (file.isDown()) {
-                    if (PreviewMediaFragment.canBePreviewed(file)) {
-                        // media preview
-                        mContainerActivity.startMediaPreview(file, 0, true);
-                    } else {
-                        // open with
-                        mContainerActivity.openFile(file);
-                    }
-                    
-                } else {
-                    // automatic download, preview on finish
-                    mContainerActivity.startDownloadForPreview(file);
-                }
-                    
-            }
-            
-        } else {
-            Log_OC.d(TAG, "Null object in ListAdapter!!");
-        }
-        
-    }
-    
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onCreateContextMenu (ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
-        super.onCreateContextMenu(menu, v, menuInfo);
-        MenuInflater inflater = getActivity().getMenuInflater();
-        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>();    
-        List<Integer> toDisable = new ArrayList<Integer>();  
-        
-        MenuItem item = null;
-        if (targetFile.isDirectory()) {
-            // contextual menu for folders
-            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_sync_file);
-            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.action_rename_file);
-                toDisable.add(R.id.action_remove_file);
-                
-            }
-            
-        } else {
-            // contextual menu for regular files
-            
-            // new design: 'download' and 'open with' won't be available anymore in context menu
-            toHide.add(R.id.action_download_file);
-            toHide.add(R.id.action_open_file_with);
-            
-            if (targetFile.isDown()) {
-                toHide.add(R.id.action_cancel_download);
-                toHide.add(R.id.action_cancel_upload);
-                
-            } else {
-                toHide.add(R.id.action_sync_file);
-            }
-            if ( mContainerActivity.getFileDownloaderBinder().isDownloading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile)) {
-                toHide.add(R.id.action_cancel_upload);
-                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.action_cancel_download);
-                toDisable.add(R.id.action_rename_file);
-                toDisable.add(R.id.action_remove_file);
-                    
-            } else {
-                toHide.add(R.id.action_cancel_download);
-                toHide.add(R.id.action_cancel_upload);
-            }
-        }
-
-        for (int i : toHide) {
-            item = menu.findItem(i);
-            if (item != null) {
-                item.setVisible(false);
-                item.setEnabled(false);
-            }
-        }
-        
-        for (int i : toDisable) {
-            item = menu.findItem(i);
-            if (item != null) {
-                item.setEnabled(false);
-            }
-        }
-    }
-    
-    
-    /**
-     * {@inhericDoc}
-     */
-    @Override
-    public boolean onContextItemSelected (MenuItem item) {
-        AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();        
-        mTargetFile = (OCFile) mAdapter.getItem(info.position);
-        switch (item.getItemId()) {
-            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.action_remove_file: {
-                int messageStringId = R.string.confirmation_remove_alert;
-                int posBtnStringId = R.string.confirmation_remove_remote;
-                int neuBtnStringId = -1;
-                if (mTargetFile.isDirectory()) {
-                    messageStringId = R.string.confirmation_remove_folder_alert;
-                    posBtnStringId = R.string.confirmation_remove_remote_and_local;
-                    neuBtnStringId = R.string.confirmation_remove_folder_local;
-                } else if (mTargetFile.isDown()) {
-                    posBtnStringId = R.string.confirmation_remove_remote_and_local;
-                    neuBtnStringId = R.string.confirmation_remove_local;
-                }
-                ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
-                        messageStringId,
-                        new String[]{mTargetFile.getFileName()},
-                        posBtnStringId,
-                        neuBtnStringId,
-                        R.string.common_cancel);
-                confDialog.setOnConfirmationListener(this);
-                confDialog.show(getFragmentManager(), FileDetailFragment.FTAG_CONFIRMATION);
-                return true;
-            }
-            case R.id.action_sync_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());
-                ((FileDisplayActivity) getSherlockActivity()).showLoadingDialog();
-                return true;
-            }
-            case R.id.action_cancel_download: {
-                FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
-                Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity());
-                if (downloaderBinder != null && downloaderBinder.isDownloading(account, mTargetFile)) {
-                    downloaderBinder.cancel(account, mTargetFile);
-                    listDirectory();
-                    mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
-                }
-                return true;
-            }
-            case R.id.action_cancel_upload: {
-                FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
-                Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity());
-                if (uploaderBinder != null && uploaderBinder.isUploading(account, mTargetFile)) {
-                    uploaderBinder.cancel(account, mTargetFile);
-                    listDirectory();
-                    mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
-                }
-                return true;
-            }
-            case R.id.action_see_details: {
-                ((FileFragment.ContainerActivity)getActivity()).showDetails(mTargetFile);
-                return true;
-            }
-            default:
-                return super.onContextItemSelected(item); 
-        }
-    }
-    
-
-    /**
-     * Use this to query the {@link OCFile} that is currently
-     * being displayed by this fragment
-     * @return The currently viewed OCFile
-     */
-    public OCFile getCurrentFile(){
-        return mFile;
-    }
-    
-    /**
-     * Calls {@link OCFileListFragment#listDirectory(OCFile)} with a null parameter
-     */
-    public void listDirectory(){
-        listDirectory(null);
-    }
-    
-    /**
-     * Lists the given directory on the view. When the input parameter is null,
-     * it will either refresh the last known directory. list the root
-     * if there never was a directory.
-     * 
-     * @param directory File to be listed
-     */
-    public void listDirectory(OCFile directory) {
-        DataStorageManager storageManager = mContainerActivity.getStorageManager();
-        if (storageManager != null) {
-
-            // Check input parameters for null
-            if(directory == null){
-                if(mFile != null){
-                    directory = mFile;
-                } else {
-                    directory = storageManager.getFileByPath("/");
-                    if (directory == null) return; // no files, wait for sync
-                }
-            }
-        
-        
-            // If that's not a directory -> List its parent
-            if(!directory.isDirectory()){
-                Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
-                directory = storageManager.getFileById(directory.getParentId());
-            }
-
-            mAdapter.swapDirectory(directory, storageManager);
-            if (mFile == null || !mFile.equals(directory)) {
-                mList.setSelectionFromTop(0, 0);
-            }
-            mFile = directory;
-
-        }
-    }
-    
-    
-    
-    /**
-     * Interface to implement by any Activity that includes some instance of FileListFragment
-     * 
-     * @author David A. Velasco
-     */
-    public interface ContainerActivity extends TransferServiceGetter, OnRemoteOperationListener, FileHandler {
-
-        /**
-         * Callback method invoked when a the user browsed into a different folder through the list of files
-         *  
-         * @param file
-         */
-        public void onBrowsedDownTo(OCFile folder);
-        
-        public void startDownloadForPreview(OCFile file);
-
-        public void startMediaPreview(OCFile file, int i, boolean b);
-
-        public void startImagePreview(OCFile file);
-
-        /**
-         * Getter for the current DataStorageManager in the container activity
-         */
-        public DataStorageManager getStorageManager();
-        
-        
-        /**
-         * Callback method invoked when a the 'transfer state' of a file changes.
-         * 
-         * This happens when a download or upload is started or ended for a file.
-         * 
-         * This method is necessary by now to update the user interface of the double-pane layout in tablets
-         * because methods {@link FileDownloaderBinder#isDownloading(Account, OCFile)} and {@link FileUploaderBinder#isUploading(Account, OCFile)}
-         * won't provide the needed response before the method where this is called finishes. 
-         * 
-         * TODO Remove this when the transfer state of a file is kept in the database (other thing TODO)
-         * 
-         * @param file          OCFile which state changed.
-         * @param downloading   Flag signaling if the file is now downloading.
-         * @param uploading     Flag signaling if the file is now uploading.
-         */
-        public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading);
-        
-    }
-    
-    
-    @Override
-    public void onDismiss(EditNameDialog dialog) {
-        if (dialog.getResult()) {
-            String newFilename = dialog.getNewFilename();
-            Log_OC.d(TAG, "name edit dialog dismissed with new name " + newFilename);
-            RemoteOperation operation = new RenameFileOperation(mTargetFile, 
-                                                                AccountUtils.getCurrentOwnCloudAccount(getActivity()), 
-                                                                newFilename, 
-                                                                mContainerActivity.getStorageManager());
-            operation.execute(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
-            ((FileDisplayActivity) getActivity()).showLoadingDialog();
-        }
-    }
-
-    
-    @Override
-    public void onConfirmation(String callerTag) {
-        if (callerTag.equals(FileDetailFragment.FTAG_CONFIRMATION)) {
-            if (mContainerActivity.getStorageManager().getFileById(mTargetFile.getFileId()) != null) {
-                RemoteOperation operation = new RemoveFileOperation( mTargetFile, 
-                                                                    true, 
-                                                                    mContainerActivity.getStorageManager());
-                operation.execute(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
-                
-                ((FileDisplayActivity) getActivity()).showLoadingDialog();
-            }
-        }
-    }
-    
-    @Override
-    public void onNeutral(String callerTag) {
-        File f = null;
-        if (mTargetFile.isDirectory()) {
-            // TODO run in a secondary thread?
-            mContainerActivity.getStorageManager().removeDirectory(mTargetFile, false, true);
-            
-        } else if (mTargetFile.isDown() && (f = new File(mTargetFile.getStoragePath())).exists()) {
-            f.delete();
-            mTargetFile.setStoragePath(null);
-            mContainerActivity.getStorageManager().saveFile(mTargetFile);
-        }
-        listDirectory();
-        mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
-    }
-    
-    @Override
-    public void onCancel(String callerTag) {
-        Log_OC.d(TAG, "REMOVAL CANCELED");
-    }
-
-
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/preview/FileDownloadFragment.java b/src/de/mobilcom/debitel/cloud/android/ui/preview/FileDownloadFragment.java
deleted file mode 100644 (file)
index e2eade6..0000000
+++ /dev/null
@@ -1,382 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.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.ImageButton;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.files.services.FileDownloader.FileDownloaderBinder;
-import de.mobilcom.debitel.cloud.android.ui.fragment.FileFragment;
-
-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 FileFragment implements OnClickListener {
-
-    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 Account mAccount;
-    
-    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() {
-        super();
-        mAccount = 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) {
-        super(fileToDetail);
-        mAccount = ocAccount;
-        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) {
-                setFile((OCFile)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);
-        
-        ((ImageButton)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, getFile());
-        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, getFile())) {
-                    downloaderBinder.cancel(mAccount, getFile());
-                    getActivity().finish(); // :)
-                    /*
-                    leaveTransferProgress();
-                    if (mFile.isDown()) {
-                        setButtonsForDown();
-                    } else {
-                        setButtonsForRemote();
-                    }
-                    */
-                }
-                break;
-            }
-            default:
-                Log_OC.e(TAG, "Incorrect view clicked!");
-        }
-    }
-
-    
-    /**
-     * 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, getFile()))) {
-            setButtonsForTransferring();
-            
-        } else if (getFile().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, getFile());
-                mListening = true;
-                setButtonsForTransferring();
-            }
-        }
-    }
-    
-    
-    public void leaveTransferProgress() {
-        if (mProgressListener != null) {
-            if (mContainerActivity.getFileDownloaderBinder() != null) {
-                mContainerActivity.getFileDownloaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, getFile());
-                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/de/mobilcom/debitel/cloud/android/ui/preview/PreviewImageActivity.java b/src/de/mobilcom/debitel/cloud/android/ui/preview/PreviewImageActivity.java
deleted file mode 100644 (file)
index 892716d..0000000
+++ /dev/null
@@ -1,467 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.preview;
-
-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.app.Fragment;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentTransaction;
-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.view.MenuItem;
-import com.actionbarsherlock.view.Window;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.files.services.FileDownloader;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader;
-import de.mobilcom.debitel.cloud.android.files.services.FileDownloader.FileDownloaderBinder;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader.FileUploaderBinder;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileActivity;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileDisplayActivity;
-import de.mobilcom.debitel.cloud.android.ui.dialog.LoadingDialog;
-import de.mobilcom.debitel.cloud.android.ui.fragment.FileFragment;
-
-/**
- *  Holds a swiping galley where image files contained in an ownCloud directory are shown
- *  
- *  @author David A. Velasco
- */
-public class PreviewImageActivity extends FileActivity 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 static final String DIALOG_WAIT_TAG = "DIALOG_WAIT";
-    
-    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;
-    
-    private String mDownloadAddedMessage;
-    private String mDownloadFinishMessage;
-    
-    
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
-        setContentView(R.layout.preview_image_activity);
-        
-        ActionBar actionBar = getSupportActionBar();
-        actionBar.setDisplayHomeAsUpEnabled(true);
-        actionBar.hide();
-        
-        mFullScreen = true;
-        if (savedInstanceState != null) {
-            mRequestWaitingForBinder = savedInstanceState.getBoolean(KEY_WAITING_FOR_BINDER);
-        } else {
-            mRequestWaitingForBinder = false;
-        }
-        
-        FileDownloader downloader = new FileDownloader();
-        mDownloadAddedMessage = downloader.getDownloadAddedMessage();
-        mDownloadFinishMessage= downloader.getDownloadFinishMessage();
-    }
-
-    private void initViewPager() {
-        // get parent from path
-        String parentPath = getFile().getRemotePath().substring(0, getFile().getRemotePath().lastIndexOf(getFile().getFileName()));
-        OCFile parentFolder = mStorageManager.getFileByPath(parentPath);
-        //OCFile parentFolder = mStorageManager.getFileById(getFile().getParentId());
-        if (parentFolder == null) {
-            // should not be necessary
-            parentFolder = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR);
-        }
-        mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(), parentFolder, getAccount(), mStorageManager);
-        mViewPager = (ViewPager) findViewById(R.id.fragmentPager);
-        int position = mPreviewImagePagerAdapter.getFilePosition(getFile());
-        position = (position >= 0) ? position : 0;
-        mViewPager.setAdapter(mPreviewImagePagerAdapter); 
-        mViewPager.setOnPageChangeListener(this);
-        mViewPager.setCurrentItem(position);
-        if (position == 0 && !getFile().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(mDownloadFinishMessage);
-        filter.addAction(mDownloadAddedMessage);
-        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() {
-        finish();
-    }
-    
-    /**
-     * Show loading dialog 
-     */
-    public void showLoadingDialog() {
-        // Construct dialog
-        LoadingDialog loading = new LoadingDialog(getResources().getString(R.string.wait_a_moment));
-        FragmentManager fm = getSupportFragmentManager();
-        FragmentTransaction ft = fm.beginTransaction();
-        loading.show(ft, DIALOG_WAIT_TAG);
-        
-    }
-    
-    /**
-     * Dismiss loading dialog
-     */
-    public void dismissLoadingDialog(){
-        Fragment frag = getSupportFragmentManager().findFragmentByTag(DIALOG_WAIT_TAG);
-      if (frag != null) {
-          LoadingDialog loading = (LoadingDialog) frag;
-            loading.dismiss();
-        }
-    }
-    
-    /**
-     * {@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 showDetails(OCFile file) {
-        Intent showDetailsIntent = new Intent(this, FileDisplayActivity.class);
-        showDetailsIntent.setAction(FileDisplayActivity.ACTION_DETAILS);
-        showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, file);
-        showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
-        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(getAccount(), file)) {
-            Intent i = new Intent(this, FileDownloader.class);
-            i.putExtra(FileDownloader.EXTRA_ACCOUNT, getAccount());
-            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 (getAccount().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(mDownloadFinishMessage)) {
-                    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;
-    }
-
-    @Override
-    protected void onAccountSet(boolean stateWasRecovered) {
-        if (getAccount() != null) {
-            OCFile file = getFile();
-            /// Validate handled file  (first image to preview)
-            if (file == null) {
-                throw new IllegalStateException("Instanced with a NULL OCFile");
-            }
-            if (!file.isImage()) {
-                throw new IllegalArgumentException("Non-image file passed as argument");
-            }
-            mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver());            
-            
-            // Update file according to DB file, if it is possible
-            if (file.getFileId() > DataStorageManager.ROOT_PARENT_ID)            
-                file = mStorageManager.getFileById(file.getFileId());
-            
-            if (file != null) {
-                /// Refresh the activity according to the Account and OCFile set
-                setFile(file);  // reset after getting it fresh from mStorageManager
-                getSupportActionBar().setTitle(getFile().getFileName());
-                //if (!stateWasRecovered) {
-                    initViewPager();
-                //}
-
-            } else {
-                // handled file not in the current Account
-                finish();
-            }
-            
-        } else {
-            Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
-        }
-    }
-    
-    
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/preview/PreviewImageFragment.java b/src/de/mobilcom/debitel/cloud/android/ui/preview/PreviewImageFragment.java
deleted file mode 100644 (file)
index 011803e..0000000
+++ /dev/null
@@ -1,631 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.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.view.Menu;
-import com.actionbarsherlock.view.MenuInflater;
-import com.actionbarsherlock.view.MenuItem;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.operations.OnRemoteOperationListener;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult;
-import de.mobilcom.debitel.cloud.android.operations.RemoveFileOperation;
-import de.mobilcom.debitel.cloud.android.ui.fragment.ConfirmationDialogFragment;
-import de.mobilcom.debitel.cloud.android.ui.fragment.FileFragment;
-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 FileFragment implements   OnRemoteOperationListener, 
-                                                                        ConfirmationDialogFragment.ConfirmationDialogFragmentListener {
-    public static final String EXTRA_FILE = "FILE";
-    public static final String EXTRA_ACCOUNT = "ACCOUNT";
-
-    private View mView;
-    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) {
-        super(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() {
-        super();
-        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) {
-                setFile((OCFile)savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_FILE));
-                mAccount = savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_ACCOUNT);
-            } else {
-                mIgnoreFirstSavedState = false;
-            }
-        }
-        if (getFile() == null) {
-            throw new IllegalStateException("Instanced with a NULL OCFile");
-        }
-        if (mAccount == null) {
-            throw new IllegalStateException("Instanced with a NULL ownCloud Account");
-        }
-        if (!getFile().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, getFile());
-        outState.putParcelable(PreviewImageFragment.EXTRA_ACCOUNT, mAccount);
-    }
-    
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        if (getFile() != null) {
-           BitmapLoader bl = new BitmapLoader(mImageView, mMessageView, mProgressWheel);
-           bl.execute(new String[]{getFile().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()).showDetails(getFile());        
-    }
-
-
-    @Override
-    public void onResume() {
-        super.onResume();
-    }
-
-
-    @Override
-    public void onPause() {
-        super.onPause();
-    }
-
-
-    @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() {
-        OCFile file = getFile();
-        String storagePath = file.getStoragePath();
-        String encodedStoragePath = WebdavUtils.encodePath(storagePath);
-        try {
-            Intent i = new Intent(Intent.ACTION_VIEW);
-            i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), file.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: " + file.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(file.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 " + file.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[]{getFile().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(getFile().getFileId()) != null) {   // check that the file is still there;
-            mLastRemoteOperation = new RemoveFileOperation( getFile(),      // 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());
-            
-            ((PreviewImageActivity) getActivity()).showLoadingDialog();
-        }
-    }
-    
-    
-    /**
-     * Removes the file from local storage
-     */
-    @Override
-    public void onNeutral(String callerTag) {
-        // TODO this code should be made in a secondary thread,
-        OCFile file = getFile();
-        if (file.isDown()) {   // checks it is still there
-            File f = new File(file.getStoragePath());
-            f.delete();
-            file.setStoragePath(null);
-            mStorageManager.saveFile(file);
-            finish();
-        }
-    }
-    
-    /**
-     * User cancelled the removal action.
-     */
-    @Override
-    public void onCancel(String callerTag) {
-        // nothing to do here
-    }
-    
-
-    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 " + getFile().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) {
-        ((PreviewImageActivity) getActivity()).dismissLoadingDialog();
-        
-        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/de/mobilcom/debitel/cloud/android/ui/preview/PreviewImagePagerAdapter.java b/src/de/mobilcom/debitel/cloud/android/ui/preview/PreviewImagePagerAdapter.java
deleted file mode 100644 (file)
index 8fcf61b..0000000
+++ /dev/null
@@ -1,332 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.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 de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.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 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;
-
-    /**
-     * 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/de/mobilcom/debitel/cloud/android/ui/preview/PreviewMediaFragment.java b/src/de/mobilcom/debitel/cloud/android/ui/preview/PreviewMediaFragment.java
deleted file mode 100644 (file)
index 89af08f..0000000
+++ /dev/null
@@ -1,769 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.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.content.res.Configuration;
-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.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.view.Menu;
-import com.actionbarsherlock.view.MenuInflater;
-import com.actionbarsherlock.view.MenuItem;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.media.MediaControlView;
-import de.mobilcom.debitel.cloud.android.media.MediaService;
-import de.mobilcom.debitel.cloud.android.media.MediaServiceBinder;
-import de.mobilcom.debitel.cloud.android.operations.OnRemoteOperationListener;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult;
-import de.mobilcom.debitel.cloud.android.operations.RemoveFileOperation;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileActivity;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileDisplayActivity;
-import de.mobilcom.debitel.cloud.android.ui.fragment.ConfirmationDialogFragment;
-import de.mobilcom.debitel.cloud.android.ui.fragment.FileFragment;
-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 FileFragment implements
-        OnTouchListener,  
-        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 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;
-    public boolean mPrepared;
-    
-    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, int startPlaybackPosition, boolean autoplay) {
-        super(fileToDetail);
-        mAccount = ocAccount;
-        mSavedPlaybackPosition = startPlaybackPosition;
-        mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment 
-        mAutoplay = autoplay;
-    }
-    
-    
-    /**
-     *  Creates an empty fragment for previews.
-     * 
-     *  MUST BE KEPT: the system uses it when tries to reinstantiate a fragment automatically (for instance, when the device is turned a aside).
-     * 
-     *  DO NOT CALL IT: an {@link OCFile} and {@link Account} must be provided for a successful construction 
-     */
-    public PreviewMediaFragment() {
-        super();
-        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);
-        Log_OC.e(TAG, "onCreateView");
-
-        
-        mView = inflater.inflate(R.layout.file_preview, container, false);
-        
-        mImagePreview = (ImageView)mView.findViewById(R.id.image_preview);
-        mVideoPreview = (VideoView)mView.findViewById(R.id.video_preview);
-        mVideoPreview.setOnTouchListener(this);
-        
-        mMediaController = (MediaControlView)mView.findViewById(R.id.media_controller);
-        
-        return mView;
-    }
-    
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onAttach(Activity activity) {
-        super.onAttach(activity);
-        Log_OC.e(TAG, "onAttach");
-        
-        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);
-        Log_OC.e(TAG, "onActivityCreated");
-
-        mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());
-        if (savedInstanceState != null) {
-            setFile((OCFile)savedInstanceState.getParcelable(PreviewMediaFragment.EXTRA_FILE));
-            mAccount = savedInstanceState.getParcelable(PreviewMediaFragment.EXTRA_ACCOUNT);
-            mSavedPlaybackPosition = savedInstanceState.getInt(PreviewMediaFragment.EXTRA_PLAY_POSITION);
-            mAutoplay = savedInstanceState.getBoolean(PreviewMediaFragment.EXTRA_PLAYING);
-            
-        }
-        OCFile file = getFile();
-        if (file == null) {
-            throw new IllegalStateException("Instanced with a NULL OCFile");
-        }
-        if (mAccount == null) {
-            throw new IllegalStateException("Instanced with a NULL ownCloud Account");
-        }
-        if (!file.isDown()) {
-            throw new IllegalStateException("There is no local file to preview");
-        }
-        if (file.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);
-        Log_OC.e(TAG, "onSaveInstanceState");
-        
-        outState.putParcelable(PreviewMediaFragment.EXTRA_FILE, getFile());
-        outState.putParcelable(PreviewMediaFragment.EXTRA_ACCOUNT, mAccount);
-        
-        if (getFile().isVideo()) {
-            mSavedPlaybackPosition = mVideoPreview.getCurrentPosition();
-            mAutoplay = mVideoPreview.isPlaying();
-            outState.putInt(PreviewMediaFragment.EXTRA_PLAY_POSITION , mSavedPlaybackPosition);
-            outState.putBoolean(PreviewMediaFragment.EXTRA_PLAYING , mAutoplay);
-        } else {
-            outState.putInt(PreviewMediaFragment.EXTRA_PLAY_POSITION , mMediaServiceBinder.getCurrentPosition());
-            outState.putBoolean(PreviewMediaFragment.EXTRA_PLAYING , mMediaServiceBinder.isPlaying());
-        }
-    }
-    
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        Log_OC.e(TAG, "onStart");
-
-        OCFile file = getFile();
-        if (file != null) {
-           if (file.isAudio()) {
-               bindMediaService();
-               
-           } else if (file.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_sync_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()).showDetails(getFile());        
-    }
-
-
-    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(getFile().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();
-            mPrepared = true;
-        }
-        
-        
-        /**
-         * 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(getFile().getStoragePath());
-                }
-            } // else : called from onError()
-            mMediaController.updatePausePlay();
-        }
-        
-        
-        /**
-         * Called when an error in playback occurs.
-         * 
-         * @param   mp      {@link MediaPlayer} instance performing the playback.
-         * @param   what    Type of error
-         * @param   extra   Extra code specific to the error
-         */
-        @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 onPause() {
-        super.onPause();
-        Log_OC.e(TAG, "onPause");
-    }
-    
-    @Override
-    public void onResume() {
-        super.onResume();
-        Log_OC.e(TAG, "onResume");
-    }
-    
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-        Log_OC.e(TAG, "onDestroy");
-    }
-    
-    @Override
-    public void onStop() {
-        Log_OC.e(TAG, "onStop");
-        super.onStop();
-
-        mPrepared = false;
-        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(FileActivity.EXTRA_ACCOUNT, mAccount);
-        i.putExtra(FileActivity.EXTRA_FILE, getFile());
-        i.putExtra(PreviewVideoActivity.EXTRA_AUTOPLAY, mVideoPreview.isPlaying());
-        mVideoPreview.pause();
-        i.putExtra(PreviewVideoActivity.EXTRA_START_POSITION, mVideoPreview.getCurrentPosition());
-        startActivityForResult(i, 0);
-    }
-
-    @Override
-    public void onConfigurationChanged (Configuration newConfig) {
-        Log_OC.e(TAG, "onConfigurationChanged " + this);
-    }
-    
-    @Override
-    public void onActivityResult (int requestCode, int resultCode, Intent data) {
-        Log_OC.e(TAG, "onActivityResult " + this);
-        super.onActivityResult(requestCode, resultCode, data);
-        if (resultCode == Activity.RESULT_OK) {
-            mSavedPlaybackPosition = data.getExtras().getInt(PreviewVideoActivity.EXTRA_START_POSITION);
-            mAutoplay = data.getExtras().getBoolean(PreviewVideoActivity.EXTRA_AUTOPLAY); 
-        }
-    }
-    
-
-    private void playAudio() {
-        OCFile file = getFile();
-        if (!mMediaServiceBinder.isPlaying(file)) {
-            Log_OC.d(TAG, "starting playback of " + file.getStoragePath());
-            mMediaServiceBinder.start(mAccount, file, mAutoplay, mSavedPlaybackPosition);
-            
-        } else {
-            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() {
-        OCFile file = getFile();
-        stopPreview(true);
-        String storagePath = file.getStoragePath();
-        String encodedStoragePath = WebdavUtils.encodePath(storagePath);
-        try {
-            Intent i = new Intent(Intent.ACTION_VIEW);
-            i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), file.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: " + file.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(file.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 " + file.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[]{getFile().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) {
-        OCFile file = getFile();
-        if (mStorageManager.getFileById(file.getFileId()) != null) {   // check that the file is still there;
-            stopPreview(true);
-            mLastRemoteOperation = new RemoveFileOperation( file,      // 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());
-            
-            ((FileDisplayActivity) getActivity()).showLoadingDialog();
-        }
-    }
-    
-    
-    /**
-     * Removes the file from local storage
-     */
-    @Override
-    public void onNeutral(String callerTag) {
-        // TODO this code should be made in a secondary thread,
-        OCFile file = getFile();
-        if (file.isDown()) {   // checks it is still there
-            stopPreview(true);
-            File f = new File(file.getStoragePath());
-            f.delete();
-            file.setStoragePath(null);
-            mStorageManager.saveFile(file);
-            finish();
-        }
-    }
-    
-    /**
-     * User cancelled the removal action.
-     */
-    @Override
-    public void onCancel(String callerTag) {
-        // nothing to do here
-    }
-    
-
-    /**
-     * 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) {
-        ((FileDisplayActivity) getActivity()).dismissLoadingDialog();
-        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) {
-        OCFile file = getFile();
-        if (file.isAudio() && stopAudio) {
-            mMediaServiceBinder.pause();
-            
-        } else if (file.isVideo()) {
-            mVideoPreview.stopPlayback();
-        }
-    }
-
-
-
-    /**
-     * Finishes the preview
-     */
-    private void finish() {
-        getActivity().onBackPressed();
-    }
-
-
-    public int getPosition() {
-        if (mPrepared) {
-            mSavedPlaybackPosition = mVideoPreview.getCurrentPosition();
-        }
-        Log_OC.e(TAG, "getting position: " + mSavedPlaybackPosition);
-        return mSavedPlaybackPosition;
-    }
-    
-    public boolean isPlaying() {
-        if (mPrepared) {
-            mAutoplay = mVideoPreview.isPlaying();
-        }
-        return mAutoplay;
-    }
-    
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/ui/preview/PreviewVideoActivity.java b/src/de/mobilcom/debitel/cloud/android/ui/preview/PreviewVideoActivity.java
deleted file mode 100644 (file)
index bedb806..0000000
+++ /dev/null
@@ -1,239 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.ui.preview;
-
-import android.accounts.Account;
-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.widget.MediaController;
-import android.widget.VideoView;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils.AccountNotFoundException;
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.media.MediaService;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileActivity;
-
-/**
- *  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 FileActivity implements OnCompletionListener, OnPreparedListener, OnErrorListener {
-
-    /** 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 DataStorageManager mStorageManager;
-    
-    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();
-            mSavedPlaybackPosition = extras.getInt(EXTRA_START_POSITION);
-            mAutoplay = extras.getBoolean(EXTRA_AUTOPLAY);
-            
-        } else {
-            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);
-    }    
-    
-    
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        Log_OC.e(TAG, "ACTIVITY\t\tonSaveInstanceState");
-        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();
-    }
-
-    
-    /** 
-     * 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;
-    }
-    
-    
-    @Override
-    protected void onAccountSet(boolean stateWasRecovered) {
-        if (getAccount() != null) {
-            OCFile file = getFile();
-            /// Validate handled file  (first image to preview)
-            if (file == null) {
-                throw new IllegalStateException("Instanced with a NULL OCFile");
-            }
-            if (!file.isVideo()) {
-                throw new IllegalArgumentException("Non-video file passed as argument");
-            }
-            mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver());
-            file = mStorageManager.getFileById(file.getFileId()); 
-            if (file != null) {
-                if (file.isDown()) {
-                    mVideoPlayer.setVideoPath(file.getStoragePath());
-                    
-                } else {
-                    // not working yet
-                    String url;
-                    try {
-                        url = AccountUtils.constructFullURLForAccount(this, getAccount()) + file.getRemotePath();
-                        mVideoPlayer.setVideoURI(Uri.parse(url));
-                    } catch (AccountNotFoundException e) {
-                        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 {
-                finish();
-            }
-        } else {
-            Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
-            finish();
-        }
-   }
-
-
-}
\ No newline at end of file
diff --git a/src/de/mobilcom/debitel/cloud/android/utils/FileStorageUtils.java b/src/de/mobilcom/debitel/cloud/android/utils/FileStorageUtils.java
deleted file mode 100644 (file)
index 1608a99..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.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 de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.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() + "/" + MainApp.getDataFolder() + "/" + 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() + "/" + MainApp.getDataFolder() + "/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 + MainApp.getDataFolder() + 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
diff --git a/src/de/mobilcom/debitel/cloud/android/utils/OwnCloudVersion.java b/src/de/mobilcom/debitel/cloud/android/utils/OwnCloudVersion.java
deleted file mode 100644 (file)
index e77f90f..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.utils;
-
-public class OwnCloudVersion implements Comparable<OwnCloudVersion> {
-    public static final OwnCloudVersion owncloud_v1 = new OwnCloudVersion(
-            0x010000);
-    public static final OwnCloudVersion owncloud_v2 = new OwnCloudVersion(
-            0x020000);
-    public static final OwnCloudVersion owncloud_v3 = new OwnCloudVersion(
-            0x030000);
-    public static final OwnCloudVersion owncloud_v4 = new OwnCloudVersion(
-            0x040000);
-    public static final OwnCloudVersion owncloud_v4_5 = new OwnCloudVersion(
-            0x040500);
-
-    // format is in version
-    // 0xAABBCC
-    // for version AA.BB.CC
-    // ie version 2.0.3 will be stored as 0x030003
-    private int mVersion;
-    private boolean mIsValid;
-
-    public OwnCloudVersion(int version) {
-        mVersion = version;
-        mIsValid = true;
-    }
-
-    public OwnCloudVersion(String version) {
-        mVersion = 0;
-        mIsValid = false;
-        parseVersionString(version);
-    }
-
-    public String toString() {
-        return ((mVersion >> 16) % 256) + "." + ((mVersion >> 8) % 256) + "."
-                + ((mVersion) % 256);
-    }
-
-    public boolean isVersionValid() {
-        return mIsValid;
-    }
-
-    @Override
-    public int compareTo(OwnCloudVersion another) {
-        return another.mVersion == mVersion ? 0
-                : another.mVersion < mVersion ? 1 : -1;
-    }
-
-    private void parseVersionString(String version) {
-        try {
-            String[] nums = version.split("\\.");
-            if (nums.length > 0) {
-                mVersion += Integer.parseInt(nums[0]);
-            }
-            mVersion = mVersion << 8;
-            if (nums.length > 1) {
-                mVersion += Integer.parseInt(nums[1]);
-            }
-            mVersion = mVersion << 8;
-            if (nums.length > 2) {
-                mVersion += Integer.parseInt(nums[2]);
-            }
-            mIsValid = true;
-        } catch (Exception e) {
-            mIsValid = false;
-        }
-    }
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/utils/RecursiveFileObserver.java b/src/de/mobilcom/debitel/cloud/android/utils/RecursiveFileObserver.java
deleted file mode 100644 (file)
index 84c9bfd..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.utils;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Stack;
-
-import android.os.FileObserver;
-
-public class RecursiveFileObserver extends FileObserver {
-
-    public static int CHANGES_ONLY = CLOSE_WRITE | MOVE_SELF | MOVED_FROM;
-    
-    List<SingleFileObserver> mObservers;
-    String mPath;
-    int mMask;
-    
-    public RecursiveFileObserver(String path) {
-        this(path, ALL_EVENTS);
-    }
-    
-    public RecursiveFileObserver(String path, int mask) {
-        super(path, mask);
-        mPath = path;
-        mMask = mask;
-    }
-
-    @Override
-    public void startWatching() {
-        if (mObservers != null) return;
-        mObservers = new ArrayList<SingleFileObserver>();
-        Stack<String> stack = new Stack<String>();
-        stack.push(mPath);
-        
-        while (!stack.empty()) {
-            String parent = stack.pop();
-            mObservers.add(new SingleFileObserver(parent, mMask));
-            File path = new File(parent);
-            File[] files = path.listFiles();
-            if (files == null) continue;
-            for (int i = 0; i < files.length; ++i) {
-                if (files[i].isDirectory() && !files[i].getName().equals(".")
-                    && !files[i].getName().equals("..")) {
-                    stack.push(files[i].getPath());
-                }
-            }
-        }
-        for (int i = 0; i < mObservers.size(); i++)
-            mObservers.get(i).startWatching();
-    }
-    
-    @Override
-    public void stopWatching() {
-        if (mObservers == null) return;
-        
-        for (int i = 0; i < mObservers.size(); ++i)
-            mObservers.get(i).stopWatching();
-
-        mObservers.clear();
-        mObservers = null;
-    }
-    
-    @Override
-    public void onEvent(int event, String path) {
-        
-    }
-    
-    private class SingleFileObserver extends FileObserver {
-        private String mPath;
-
-        public SingleFileObserver(String path, int mask) {
-            super(path, mask);
-            mPath = path;
-        }
-        
-        @Override
-        public void onEvent(int event, String path) {
-            String newPath = mPath + "/" + path;
-            RecursiveFileObserver.this.onEvent(event, newPath);
-        } 
-        
-    }
-}
diff --git a/src/de/mobilcom/debitel/cloud/android/widgets/ActionEditText.java b/src/de/mobilcom/debitel/cloud/android/widgets/ActionEditText.java
deleted file mode 100644 (file)
index 039ca3a..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.widgets;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import de.mobilcom.debitel.cloud.android.R;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.widget.EditText;
-
-public class ActionEditText extends EditText {
-    private String s;
-    private String optionOneString;
-    private int optionOneColor;
-    private String optionTwoString;
-    private int optionTwoColor;
-    private Rect mTextBounds, mButtonRect;
-
-    private String badgeClickCallback;
-    private Rect btn_rect;
-
-    public ActionEditText(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        getAttrs(attrs);
-        s = optionOneString;
-        mTextBounds = new Rect();
-        mButtonRect = new Rect();
-    }
-
-    public ActionEditText(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        getAttrs(attrs);
-        s = optionOneString;
-        mTextBounds = new Rect();
-        mButtonRect = new Rect();
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-
-        Paint p = getPaint();
-
-        p.getTextBounds(s, 0, s.length(), mTextBounds);
-
-        getDrawingRect(mButtonRect);
-        mButtonRect.top += 10;
-        mButtonRect.bottom -= 10;
-        mButtonRect.left = (int) (getWidth() - mTextBounds.width() - 18);
-        mButtonRect.right = getWidth() - 10;
-        btn_rect = mButtonRect;
-
-        if (s.equals(optionOneString))
-            p.setColor(optionOneColor);
-        else
-            p.setColor(optionTwoColor);
-        canvas.drawRect(mButtonRect, p);
-        p.setColor(Color.GRAY);
-
-        canvas.drawText(s, mButtonRect.left + 3, mButtonRect.bottom
-                - (mTextBounds.height() / 2), p);
-
-        invalidate();
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        int touchX = (int) event.getX();
-        int touchY = (int) event.getY();
-        boolean r = super.onTouchEvent(event);
-        if (event.getAction() == MotionEvent.ACTION_UP) {
-            if (btn_rect.contains(touchX, touchY)) {
-                if (s.equals(optionTwoString))
-                    s = optionOneString;
-                else
-                    s = optionTwoString;
-                if (badgeClickCallback != null) {
-                    @SuppressWarnings("rawtypes")
-                    Class[] paramtypes = new Class[2];
-                    paramtypes[0] = android.view.View.class;
-                    paramtypes[1] = String.class;
-                    Method method;
-                    try {
-
-                        method = getContext().getClass().getMethod(
-                                badgeClickCallback, paramtypes);
-                        method.invoke(getContext(), this, s);
-
-                    } catch (NoSuchMethodException e) {
-                        e.printStackTrace();
-                    } catch (IllegalArgumentException e) {
-                        e.printStackTrace();
-                    } catch (IllegalAccessException e) {
-                        e.printStackTrace();
-                    } catch (InvocationTargetException e) {
-                        e.printStackTrace();
-                    }
-
-                    invalidate();
-                }
-            }
-        }
-        return r;
-    }
-
-    private void getAttrs(AttributeSet attr) {
-        TypedArray a = getContext().obtainStyledAttributes(attr,
-                R.styleable.ActionEditText);
-        optionOneString = a
-                .getString(R.styleable.ActionEditText_optionOneString);
-        optionTwoString = a
-                .getString(R.styleable.ActionEditText_optionTwoString);
-        optionOneColor = a.getColor(R.styleable.ActionEditText_optionOneColor,
-                0x00ff00);
-        optionTwoColor = a.getColor(R.styleable.ActionEditText_optionTwoColor,
-                0xff0000);
-        badgeClickCallback = a
-                .getString(R.styleable.ActionEditText_onBadgeClick);
-    }
-
-}
index 8620247..a91b64e 100644 (file)
@@ -29,8 +29,9 @@ import java.util.Set;
 
 import org.apache.commons.httpclient.methods.RequestEntity;
 
 
 import org.apache.commons.httpclient.methods.RequestEntity;
 
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.network.ProgressiveDataTransferer;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.network.ProgressiveDataTransferer;
+
 
 import eu.alefzero.webdav.OnDatatransferProgressListener;
 
 
 import eu.alefzero.webdav.OnDatatransferProgressListener;
 
index 9754ef6..1d525c4 100644 (file)
@@ -31,8 +31,9 @@ import java.util.Set;
 
 import org.apache.commons.httpclient.methods.RequestEntity;
 
 
 import org.apache.commons.httpclient.methods.RequestEntity;
 
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.network.ProgressiveDataTransferer;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.network.ProgressiveDataTransferer;
+
 
 import eu.alefzero.webdav.OnDatatransferProgressListener;
 
 
 import eu.alefzero.webdav.OnDatatransferProgressListener;
 
index 8c54a53..6cb4b83 100644 (file)
@@ -41,11 +41,12 @@ import org.apache.commons.httpclient.params.HttpMethodParams;
 import org.apache.http.HttpStatus;
 import org.apache.http.params.CoreProtocolPNames;
 
 import org.apache.http.HttpStatus;
 import org.apache.http.params.CoreProtocolPNames;
 
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.network.BearerAuthScheme;
+import com.owncloud.android.network.BearerCredentials;
+
 
 
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.network.BearerAuthScheme;
-import de.mobilcom.debitel.cloud.android.network.BearerCredentials;
 
 import android.net.Uri;
 
 
 import android.net.Uri;
 
index 3ed8938..bdf1b35 100644 (file)
@@ -23,7 +23,8 @@ import org.apache.jackrabbit.webdav.property.DavProperty;
 import org.apache.jackrabbit.webdav.property.DavPropertyName;
 import org.apache.jackrabbit.webdav.property.DavPropertySet;
 
 import org.apache.jackrabbit.webdav.property.DavPropertyName;
 import org.apache.jackrabbit.webdav.property.DavPropertySet;
 
-import de.mobilcom.debitel.cloud.android.Log_OC;
+import com.owncloud.android.Log_OC;
+
 
 import android.net.Uri;
 
 
 import android.net.Uri;
 
index f78829d..b6312ef 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- package name must be unique so suffix with "tests" so package loader doesn't ignore us -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 <?xml version="1.0" encoding="utf-8"?>
 <!-- package name must be unique so suffix with "tests" so package loader doesn't ignore us -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="de.mobilcom.debitel.cloud.androidcloud.android.test"
+          package="com.owncloud.androidcloud.android.test"
           android:versionCode="1"
           android:versionName="1.0">
     <!-- We add an application tag here just so that we can indicate that
           android:versionCode="1"
           android:versionName="1.0">
     <!-- We add an application tag here just so that we can indicate that
@@ -16,6 +16,6 @@
     "ade.mobilcom.debitel.cloud.androidnt -w de.mobilcom.debitel.cloud.android.tests/android.test.InstrumentationTestRunner"
     -->
     <instrumentation android:name="android.test.InstrumentationTestRunner"
     "ade.mobilcom.debitel.cloud.androidnt -w de.mobilcom.debitel.cloud.android.tests/android.test.InstrumentationTestRunner"
     -->
     <instrumentation android:name="android.test.InstrumentationTestRunner"
-     android:targetPackage="de.mobilcom.debitel.cloud.android"
-     android:label="Tests for de.mobilcom.debitel.cloud.android"/>
+     android:targetPackage="com.owncloud.android"
+     android:label="Tests for com.owncloud.android"/>
 </manifest>
 </manifest>
diff --git a/tests/src/com/owncloud/android/test/AccountUtilsTest.java b/tests/src/com/owncloud/android/test/AccountUtilsTest.java
new file mode 100644 (file)
index 0000000..0af9183
--- /dev/null
@@ -0,0 +1,58 @@
+/* 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.test;
+
+import com.owncloud.android.authentication.Accocom.owncloud.androiddroid.utils.OwnCloudVersion;
+
+importcom.owncloud.android
+
+public class AccountUtilsTest extends AndroidTestCase {
+    
+    public void testGetWebdavPathAndOCVersion() {
+        OwnCloudVersion ocv12 = new OwnCloudVersion(0x010200);
+        OwnCloudVersion ocv12s = new OwnCloudVersion("1.2");
+        OwnCloudVersion ocv22 = new OwnCloudVersion(0x020200);
+        OwnCloudVersion ocv30 = new OwnCloudVersion(0x030000);
+        OwnCloudVersion ocv33s = new OwnCloudVersion("3.3.3");
+        OwnCloudVersion ocv45 = new OwnCloudVersion(0x040500);
+        OwnCloudVersion ocv70 = new OwnCloudVersion(0x070000);
+
+        assertTrue(AccountUtils.getWebdavPath(ocv12, false, false).equals("/webdav/owncloud.php"));
+        assertTrue(AccountUtils.getWebdavPath(ocv12s, false, false).equals("/webdav/owncloud.php"));
+        assertTrue(AccountUtils.getWebdavPath(ocv22, false, false).equals("/files/webdav.php"));
+        assertTrue(AccountUtils.getWebdavPath(ocv30,false, false).equals("/files/webdav.php"));
+        assertTrue(AccountUtils.getWebdavPath(ocv33s, false, false).equals("/files/webdav.php"));
+        assertTrue(AccountUtils.getWebdavPath(ocv45, false, false).equals("/remote.php/webdav"));
+        assertTrue(AccountUtils.getWebdavPath(ocv70, false, false).equals("/remote.php/webdav"));
+        assertNull(AccountUtils.getWebdavPath(null, false, false));
+        assertTrue(AccountUtils.getWebdavPath(ocv12, true, false).equals("/remote.php/odav"));
+        assertTrue(AccountUtils.getWebdavPath(ocv12s, true, false).equals("/remote.php/odav"));
+        assertTrue(AccountUtils.getWebdavPath(ocv22, true, false).equals("/remote.php/odav"));
+        assertTrue(AccountUtils.getWebdavPath(ocv30, true, false).equals("/remote.php/odav"));
+        assertTrue(AccountUtils.getWebdavPath(ocv33s, true, false).equals("/remote.php/odav"));
+        assertTrue(AccountUtils.getWebdavPath(ocv45, true, false).equals("/remote.php/odav"));
+        assertTrue(AccountUtils.getWebdavPath(ocv70, true, false).equals("/remote.php/odav"));
+
+        OwnCloudVersion invalidVer = new OwnCloudVersion("a.b.c");
+        assertFalse(invalidVer.isVersionValid());
+
+        assertTrue(ocv45.toString().equals("4.5.0"));
+    }
+
+}
diff --git a/tests/src/com/owncloud/android/test/FileContentProviderTest.java b/tests/src/com/owncloud/android/test/FileContentProviderTest.java
new file mode 100644 (file)
index 0000000..f68dc23
--- /dev/null
@@ -0,0 +1,52 @@
+package com.owncloud.android.test;
+
+import com.owncloud.androideta.ProviderTableMeta;
+import com.owncloud.com.owncloud.androidider;
+
+import android.annotation.TargetApi;
+import android.net.Uri;
+import android.os.Build;
+import android.test.ProviderTestCase2;
+import android.test.mock.MockContentResolver;
+import android.util.Log;
+
+@TargetApi(Build.VERSION_CODES.CUPCAKE)
+public class FileContentProviderTest extends ProviderTestCase2<FileContentProvider> {
+
+       private static final String TAG = FileContentProvider.class.getName();
+       
+       private static MockContentResolver resolve;
+       
+       public FileContentProviderTest(Class<FileContentProvider> providerClass,
+                       String providerAuthority) {
+               super(providerClass, providerAuthority);
+               // TODO Auto-generated constructor stub
+       }
+       
+       public FileContentProviderTest() {
+               super(FileContentProvider.class, "com.owncloud.android.provicom.owncloud.android        
+       @Override
+       public void setUp() {
+               Log.i(TAG, "Entered setup");
+               try {
+                       super.setUp();
+                       resolve = this.getMockContentResolver();
+               } catch (Exception e) {
+                       
+               }
+       }
+       
+       public void testGetTypeFile() {
+               Uri testuri = Uri.parse("content://org.owncloud/file/");
+               assertEquals(ProviderTableMeta.CONTENT_TYPE_ITEM, resolve.getType(testuri));
+
+               testuri = Uri.parse("content://org.owncloud/file/123");
+               assertEquals(ProviderTableMeta.CONTENT_TYPE_ITEM, resolve.getType(testuri));
+       }
+       
+       public void testGetTypeRoot() {
+               Uri testuri = Uri.parse("content://org.owncloud/");
+               assertEquals(ProviderTableMeta.CONTENT_TYPE, resolve.getType(testuri));
+       }
+
+}
diff --git a/tests/src/de/mobilcom/debitel/cloud/android/test/AccountUtilsTest.java b/tests/src/de/mobilcom/debitel/cloud/android/test/AccountUtilsTest.java
deleted file mode 100644 (file)
index 976b3b3..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/* 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 de.mobilcom.debitel.cloud.android.test;
-
-import android.test.AndroidTestCase;
-
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-import de.mobilcom.debitel.cloud.android.utils.OwnCloudVersion;
-
-public class AccountUtilsTest extends AndroidTestCase {
-    
-    public void testGetWebdavPathAndOCVersion() {
-        OwnCloudVersion ocv12 = new OwnCloudVersion(0x010200);
-        OwnCloudVersion ocv12s = new OwnCloudVersion("1.2");
-        OwnCloudVersion ocv22 = new OwnCloudVersion(0x020200);
-        OwnCloudVersion ocv30 = new OwnCloudVersion(0x030000);
-        OwnCloudVersion ocv33s = new OwnCloudVersion("3.3.3");
-        OwnCloudVersion ocv45 = new OwnCloudVersion(0x040500);
-        OwnCloudVersion ocv70 = new OwnCloudVersion(0x070000);
-
-        assertTrue(AccountUtils.getWebdavPath(ocv12, false, false).equals("/webdav/owncloud.php"));
-        assertTrue(AccountUtils.getWebdavPath(ocv12s, false, false).equals("/webdav/owncloud.php"));
-        assertTrue(AccountUtils.getWebdavPath(ocv22, false, false).equals("/files/webdav.php"));
-        assertTrue(AccountUtils.getWebdavPath(ocv30,false, false).equals("/files/webdav.php"));
-        assertTrue(AccountUtils.getWebdavPath(ocv33s, false, false).equals("/files/webdav.php"));
-        assertTrue(AccountUtils.getWebdavPath(ocv45, false, false).equals("/remote.php/webdav"));
-        assertTrue(AccountUtils.getWebdavPath(ocv70, false, false).equals("/remote.php/webdav"));
-        assertNull(AccountUtils.getWebdavPath(null, false, false));
-        assertTrue(AccountUtils.getWebdavPath(ocv12, true, false).equals("/remote.php/odav"));
-        assertTrue(AccountUtils.getWebdavPath(ocv12s, true, false).equals("/remote.php/odav"));
-        assertTrue(AccountUtils.getWebdavPath(ocv22, true, false).equals("/remote.php/odav"));
-        assertTrue(AccountUtils.getWebdavPath(ocv30, true, false).equals("/remote.php/odav"));
-        assertTrue(AccountUtils.getWebdavPath(ocv33s, true, false).equals("/remote.php/odav"));
-        assertTrue(AccountUtils.getWebdavPath(ocv45, true, false).equals("/remote.php/odav"));
-        assertTrue(AccountUtils.getWebdavPath(ocv70, true, false).equals("/remote.php/odav"));
-
-        OwnCloudVersion invalidVer = new OwnCloudVersion("a.b.c");
-        assertFalse(invalidVer.isVersionValid());
-
-        assertTrue(ocv45.toString().equals("4.5.0"));
-    }
-
-}
diff --git a/tests/src/de/mobilcom/debitel/cloud/android/test/FileContentProviderTest.java b/tests/src/de/mobilcom/debitel/cloud/android/test/FileContentProviderTest.java
deleted file mode 100644 (file)
index 21daef0..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-package de.mobilcom.debitel.cloud.android.test;
-
-import de.mobilcom.debitel.cloud.android.db.ProviderMeta.ProviderTableMeta;
-import de.mobilcom.debitel.cloud.android.providers.FileContentProvider;
-
-import android.annotation.TargetApi;
-import android.net.Uri;
-import android.os.Build;
-import android.test.ProviderTestCase2;
-import android.test.mock.MockContentResolver;
-import android.util.Log;
-
-@TargetApi(Build.VERSION_CODES.CUPCAKE)
-public class FileContentProviderTest extends ProviderTestCase2<FileContentProvider> {
-
-       private static final String TAG = FileContentProvider.class.getName();
-       
-       private static MockContentResolver resolve;
-       
-       public FileContentProviderTest(Class<FileContentProvider> providerClass,
-                       String providerAuthority) {
-               super(providerClass, providerAuthority);
-               // TODO Auto-generated constructor stub
-       }
-       
-       public FileContentProviderTest() {
-               super(FileContentProvider.class, "de.mobilcom.debitel.cloud.android.providers.FileContentProvider");
-       }
-       
-       @Override
-       public void setUp() {
-               Log.i(TAG, "Entered setup");
-               try {
-                       super.setUp();
-                       resolve = this.getMockContentResolver();
-               } catch (Exception e) {
-                       
-               }
-       }
-       
-       public void testGetTypeFile() {
-               Uri testuri = Uri.parse("content://org.owncloud/file/");
-               assertEquals(ProviderTableMeta.CONTENT_TYPE_ITEM, resolve.getType(testuri));
-
-               testuri = Uri.parse("content://org.owncloud/file/123");
-               assertEquals(ProviderTableMeta.CONTENT_TYPE_ITEM, resolve.getType(testuri));
-       }
-       
-       public void testGetTypeRoot() {
-               Uri testuri = Uri.parse("content://org.owncloud/");
-               assertEquals(ProviderTableMeta.CONTENT_TYPE, resolve.getType(testuri));
-       }
-
-}